diff options
author | Gabriel de Dietrich <gabriel.dietrich-de@nokia.com> | 2011-02-08 10:57:34 (GMT) |
---|---|---|
committer | Gabriel de Dietrich <gabriel.dietrich-de@nokia.com> | 2011-02-08 10:57:34 (GMT) |
commit | eb7958c65b09569c2531d2c443a6d0fe0b507d97 (patch) | |
tree | 91544e4a4cf5c65b3acec7c219ebe63176a14994 /src | |
parent | ef998bca70a799806c5754edfa49ea625881bc3e (diff) | |
parent | 44298c848ac254fe1942eb32eed7651dec5bf0e3 (diff) | |
download | Qt-eb7958c65b09569c2531d2c443a6d0fe0b507d97.zip Qt-eb7958c65b09569c2531d2c443a6d0fe0b507d97.tar.gz Qt-eb7958c65b09569c2531d2c443a6d0fe0b507d97.tar.bz2 |
Merge branch 'master-upstream'
Diffstat (limited to 'src')
215 files changed, 11602 insertions, 3544 deletions
diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/ChangeLog b/src/3rdparty/javascriptcore/JavaScriptCore/ChangeLog index fd6c3f7..9cbf0c1 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/ChangeLog +++ b/src/3rdparty/javascriptcore/JavaScriptCore/ChangeLog @@ -232,6 +232,34 @@ without using doubles, but the code would be much more complicated, and there is no important reason to stick to integers here. +2010-03-22 Siddharth Mathur <siddharth.mathur@nokia.com> + + Reviewed by Laszlo Gombos. + + [Symbian] More efficient aligned memory allocation for JSC Collector + https://bugs.webkit.org/show_bug.cgi?id=34350 + + * JavaScriptCore.pri: Added 2 new Symbian source files and HAL linkage + + * runtime/Collector.cpp: Reduced port-specific code and added private data member + (JSC::Heap::Heap): + (JSC::Heap::~Heap): + (JSC::Heap::destroy): + (JSC::Heap::allocateBlock): + (JSC::Heap::freeBlockPtr): + + * runtime/Collector.h: Added private data member + + * wtf/symbian: Added. + * wtf/symbian/BlockAllocatorSymbian.cpp: Added. + (WTF::AlignedBlockAllocator::AlignedBlockAllocator): Helper class to allocate + aligned blocks more efficiently as required by Collector + (WTF::AlignedBlockAllocator::alloc): + (WTF::AlignedBlockAllocator::free): + (WTF::AlignedBlockAllocator::destroy): + (WTF::AlignedBlockAllocator::~AlignedBlockAllocator): + * wtf/symbian/BlockAllocatorSymbian.h: Added. + 2010-03-22 Geoffrey Garen <ggaren@apple.com> Reviewed by Sam Weinig. @@ -330,6 +358,31 @@ * wtf/AlwaysInline.h: +2010-02-12 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Darin Adler. + + https://bugs.webkit.org/show_bug.cgi?id=33731 + Many false leaks in release builds due to PtrAndFlags + + Remove UntypedPtrAndBitfield (similar to PtrAndFlags) in UStringImpl, + and steal bits from the refCount instead. + + * runtime/UStringImpl.cpp: + (JSC::UStringImpl::baseSharedBuffer): + (JSC::UStringImpl::~UStringImpl): + * runtime/UStringImpl.h: + (JSC::UStringImpl::cost): + (JSC::UStringImpl::isIdentifier): + (JSC::UStringImpl::setIsIdentifier): + (JSC::UStringImpl::ref): + (JSC::UStringImpl::deref): + (JSC::UStringImpl::UStringImpl): + (JSC::UStringImpl::bufferOwnerString): + (JSC::UStringImpl::bufferOwnership): + (JSC::UStringImpl::isStatic): + (JSC::UStringImpl::): + 2010-02-12 Kwang Yul Seo <skyul@company100.net> Reviewed by Adam Barth. diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri b/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri index d75bd31..b061321 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri +++ b/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri @@ -16,6 +16,7 @@ CONFIG(debug, debug|release) { symbian: { # Need to guarantee this comes before system includes of /epoc32/include MMP_RULES += "USERINCLUDE ../JavaScriptCore/profiler" + LIBS += -lhal } INCLUDEPATH = \ @@ -33,6 +34,7 @@ INCLUDEPATH = \ $$PWD/runtime \ $$PWD/wrec \ $$PWD/wtf \ + $$PWD/wtf/symbian \ $$PWD/wtf/unicode \ $$PWD/yarr \ $$PWD/API \ @@ -211,6 +213,7 @@ SOURCES += \ wtf/qt/ThreadingQt.cpp \ wtf/RandomNumber.cpp \ wtf/RefCountedLeakCounter.cpp \ + wtf/symbian/BlockAllocatorSymbian.cpp \ wtf/symbian/RegisterFileAllocatorSymbian.cpp \ wtf/ThreadingNone.cpp \ wtf/Threading.cpp \ diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Collector.cpp b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Collector.cpp index 24873c8..42e2a35 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Collector.cpp +++ b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Collector.cpp @@ -53,11 +53,6 @@ #include <mach/thread_act.h> #include <mach/vm_map.h> -#elif OS(SYMBIAN) -#include <e32std.h> -#include <e32cmn.h> -#include <unistd.h> - #elif OS(WINDOWS) #include <windows.h> @@ -109,11 +104,6 @@ const size_t ALLOCATIONS_PER_COLLECTION = 3600; // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>. #define MIN_ARRAY_SIZE (static_cast<size_t>(14)) -#if OS(SYMBIAN) -const size_t MAX_NUM_BLOCKS = 256; // Max size of collector heap set to 16 MB -static RHeap* userChunk = 0; -#endif - #if ENABLE(JSC_MULTIPLE_THREADS) #if OS(DARWIN) @@ -146,29 +136,11 @@ Heap::Heap(JSGlobalData* globalData) , m_currentThreadRegistrar(0) #endif , m_globalData(globalData) +#if OS(SYMBIAN) + , m_blockallocator(JSCCOLLECTOR_VIRTUALMEM_RESERVATION, BLOCK_SIZE) +#endif { ASSERT(globalData); - -#if OS(SYMBIAN) - // Symbian OpenC supports mmap but currently not the MAP_ANON flag. - // Using fastMalloc() does not properly align blocks on 64k boundaries - // and previous implementation was flawed/incomplete. - // UserHeap::ChunkHeap allows allocation of continuous memory and specification - // of alignment value for (symbian) cells within that heap. - // - // Clarification and mapping of terminology: - // RHeap (created by UserHeap::ChunkHeap below) is continuos memory chunk, - // which can dynamically grow up to 8 MB, - // that holds all CollectorBlocks of this session (static). - // Each symbian cell within RHeap maps to a 64kb aligned CollectorBlock. - // JSCell objects are maintained as usual within CollectorBlocks. - if (!userChunk) { - userChunk = UserHeap::ChunkHeap(0, 0, MAX_NUM_BLOCKS * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); - if (!userChunk) - CRASH(); - } -#endif // OS(SYMBIAN) - memset(&m_heap, 0, sizeof(CollectorHeap)); allocateBlock(); } @@ -211,7 +183,9 @@ void Heap::destroy() t = next; } #endif - +#if OS(SYMBIAN) + m_blockallocator.destroy(); +#endif m_globalData = 0; } @@ -221,11 +195,9 @@ NEVER_INLINE CollectorBlock* Heap::allocateBlock() vm_address_t address = 0; vm_map(current_task(), &address, BLOCK_SIZE, BLOCK_OFFSET_MASK, VM_FLAGS_ANYWHERE | VM_TAG_FOR_COLLECTOR_MEMORY, MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_DEFAULT); #elif OS(SYMBIAN) - // Allocate a 64 kb aligned CollectorBlock - unsigned char* mask = reinterpret_cast<unsigned char*>(userChunk->Alloc(BLOCK_SIZE)); - if (!mask) + void* address = m_blockallocator.alloc(); + if (!address) CRASH(); - uintptr_t address = reinterpret_cast<uintptr_t>(mask); #elif OS(WINCE) void* address = VirtualAlloc(NULL, BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); #elif OS(WINDOWS) @@ -316,7 +288,7 @@ NEVER_INLINE void Heap::freeBlockPtr(CollectorBlock* block) #if OS(DARWIN) vm_deallocate(current_task(), reinterpret_cast<vm_address_t>(block), BLOCK_SIZE); #elif OS(SYMBIAN) - userChunk->Free(reinterpret_cast<TAny*>(block)); + m_blockallocator.free(reinterpret_cast<void*>(block)); #elif OS(WINCE) VirtualFree(block, 0, MEM_RELEASE); #elif OS(WINDOWS) diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Collector.h b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Collector.h index 7f7a679..d3616dc 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Collector.h +++ b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/Collector.h @@ -35,6 +35,10 @@ #include <pthread.h> #endif +#if OS(SYMBIAN) +#include <wtf/symbian/BlockAllocatorSymbian.h> +#endif + #define ASSERT_CLASS_FITS_IN_CELL(class) COMPILE_ASSERT(sizeof(class) <= CELL_SIZE, class_fits_in_cell) namespace JSC { @@ -167,6 +171,11 @@ namespace JSC { pthread_key_t m_currentThreadRegistrar; #endif +#if OS(SYMBIAN) + // Allocates collector blocks with correct alignment + WTF::AlignedBlockAllocator m_blockallocator; +#endif + JSGlobalData* m_globalData; }; diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.cpp b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.cpp index 4b0d1c9..4fde49e 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.cpp +++ b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.cpp @@ -38,12 +38,14 @@ namespace JSC { SharedUChar* UStringImpl::baseSharedBuffer() { ASSERT((bufferOwnership() == BufferShared) - || ((bufferOwnership() == BufferOwned) && !m_dataBuffer.asPtr<void*>())); + || ((bufferOwnership() == BufferOwned) && !m_buffer)); - if (bufferOwnership() != BufferShared) - m_dataBuffer = UntypedPtrAndBitfield(SharedUChar::create(new OwnFastMallocPtr<UChar>(m_data)).releaseRef(), BufferShared); + if (bufferOwnership() != BufferShared) { + m_refCountAndFlags = (m_refCountAndFlags & ~s_refCountMaskBufferOwnership) | BufferShared; + m_bufferShared = SharedUChar::create(new OwnFastMallocPtr<UChar>(m_data)).releaseRef(); + } - return m_dataBuffer.asPtr<SharedUChar*>(); + return m_bufferShared; } SharedUChar* UStringImpl::sharedBuffer() @@ -71,10 +73,10 @@ UStringImpl::~UStringImpl() if (bufferOwnership() == BufferOwned) fastFree(m_data); else if (bufferOwnership() == BufferSubstring) - m_dataBuffer.asPtr<UStringImpl*>()->deref(); + m_bufferSubstring->deref(); else { ASSERT(bufferOwnership() == BufferShared); - m_dataBuffer.asPtr<SharedUChar*>()->deref(); + m_bufferShared->deref(); } } } diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.h b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.h index 4e1ddc7..e6d1a8a 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.h +++ b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.h @@ -40,48 +40,6 @@ class IdentifierTable; typedef CrossThreadRefCounted<OwnFastMallocPtr<UChar> > SharedUChar; -class UntypedPtrAndBitfield { -public: - UntypedPtrAndBitfield() {} - - UntypedPtrAndBitfield(void* ptrValue, uintptr_t bitValue) - : m_value(reinterpret_cast<uintptr_t>(ptrValue) | bitValue) -#ifndef NDEBUG - , m_leaksPtr(ptrValue) -#endif - { - ASSERT(ptrValue == asPtr<void*>()); - ASSERT((*this & ~s_alignmentMask) == bitValue); - } - - template<typename T> - T asPtr() const { return reinterpret_cast<T>(m_value & s_alignmentMask); } - - UntypedPtrAndBitfield& operator&=(uintptr_t bits) - { - m_value &= bits | s_alignmentMask; - return *this; - } - - UntypedPtrAndBitfield& operator|=(uintptr_t bits) - { - m_value |= bits & ~s_alignmentMask; - return *this; - } - - uintptr_t operator&(uintptr_t mask) const - { - return m_value & mask & ~s_alignmentMask; - } - -private: - static const uintptr_t s_alignmentMask = ~static_cast<uintptr_t>(0x7); - uintptr_t m_value; -#ifndef NDEBUG - void* m_leaksPtr; // Only used to allow tools like leaks on OSX to detect that the memory is referenced. -#endif -}; - class UStringImpl : Noncopyable { public: template<size_t inlineCapacity> @@ -151,21 +109,27 @@ public: { // For substrings, return the cost of the base string. if (bufferOwnership() == BufferSubstring) - return m_dataBuffer.asPtr<UStringImpl*>()->cost(); + return m_bufferSubstring->cost(); - if (m_dataBuffer & s_reportedCostBit) + if (m_refCountAndFlags & s_refCountFlagHasReportedCost) return 0; - m_dataBuffer |= s_reportedCostBit; + m_refCountAndFlags |= s_refCountFlagHasReportedCost; return m_length; } unsigned hash() const { if (!m_hash) m_hash = computeHash(data(), m_length); return m_hash; } unsigned existingHash() const { ASSERT(m_hash); return m_hash; } // fast path for Identifiers void setHash(unsigned hash) { ASSERT(hash == computeHash(data(), m_length)); m_hash = hash; } // fast path for Identifiers - bool isIdentifier() const { return m_isIdentifier; } - void setIsIdentifier(bool isIdentifier) { m_isIdentifier = isIdentifier; } + bool isIdentifier() const { return m_refCountAndFlags & s_refCountFlagIsIdentifier; } + void setIsIdentifier(bool isIdentifier) + { + if (isIdentifier) + m_refCountAndFlags |= s_refCountFlagIsIdentifier; + else + m_refCountAndFlags &= ~s_refCountFlagIsIdentifier; + } - UStringImpl* ref() { m_refCount += s_refCountIncrement; return this; } - ALWAYS_INLINE void deref() { if (!(m_refCount -= s_refCountIncrement)) delete this; } + UStringImpl* ref() { m_refCountAndFlags += s_refCountIncrement; return this; } + ALWAYS_INLINE void deref() { m_refCountAndFlags -= s_refCountIncrement; if (!(m_refCountAndFlags & s_refCountMask)) delete this; } static void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) { @@ -205,11 +169,10 @@ private: // Used to construct normal strings with an internal or external buffer. UStringImpl(UChar* data, int length, BufferOwnership ownership) : m_data(data) + , m_buffer(0) , m_length(length) - , m_refCount(s_refCountIncrement) + , m_refCountAndFlags(s_refCountIncrement | ownership) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(0, ownership) { ASSERT((ownership == BufferInternal) || (ownership == BufferOwned)); checkConsistency(); @@ -221,11 +184,10 @@ private: enum StaticStringConstructType { ConstructStaticString }; UStringImpl(UChar* data, int length, StaticStringConstructType) : m_data(data) + , m_buffer(0) , m_length(length) - , m_refCount(s_staticRefCountInitialValue) + , m_refCountAndFlags(s_refCountFlagStatic | BufferOwned) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(0, BufferOwned) { checkConsistency(); } @@ -233,28 +195,26 @@ private: // Used to create new strings that are a substring of an existing string. UStringImpl(UChar* data, int length, PassRefPtr<UStringImpl> base) : m_data(data) + , m_bufferSubstring(base.releaseRef()) , m_length(length) - , m_refCount(s_refCountIncrement) + , m_refCountAndFlags(s_refCountIncrement | BufferSubstring) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(base.releaseRef(), BufferSubstring) { // Do use static strings as a base for substrings; UntypedPtrAndBitfield assumes // that all pointers will be at least 8-byte aligned, we cannot guarantee that of // UStringImpls that are not heap allocated. - ASSERT(m_dataBuffer.asPtr<UStringImpl*>()->size()); - ASSERT(!m_dataBuffer.asPtr<UStringImpl*>()->isStatic()); + ASSERT(m_bufferSubstring->size()); + ASSERT(!m_bufferSubstring->isStatic()); checkConsistency(); } // Used to construct new strings sharing an existing shared buffer. UStringImpl(UChar* data, int length, PassRefPtr<SharedUChar> sharedBuffer) : m_data(data) + , m_bufferShared(sharedBuffer.releaseRef()) , m_length(length) - , m_refCount(s_refCountIncrement) + , m_refCountAndFlags(s_refCountIncrement | BufferShared) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(sharedBuffer.releaseRef(), BufferShared) { checkConsistency(); } @@ -277,26 +237,31 @@ private: // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings. static const int s_minLengthToShare = 10; static const unsigned s_copyCharsInlineCutOff = 20; - static const uintptr_t s_bufferOwnershipMask = 3; - static const uintptr_t s_reportedCostBit = 4; // We initialize and increment/decrement the refCount for all normal (non-static) strings by the value 2. // We initialize static strings with an odd number (specifically, 1), such that the refCount cannot reach zero. - static const int s_refCountIncrement = 2; - static const int s_staticRefCountInitialValue = 1; - - UStringImpl* bufferOwnerString() { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr<UStringImpl*>() : this; } - const UStringImpl* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr<UStringImpl*>() : this; } + static const unsigned s_refCountMask = 0xFFFFFFF0; + static const int s_refCountIncrement = 0x20; + static const int s_refCountFlagStatic = 0x10; + static const unsigned s_refCountFlagHasReportedCost = 0x8; + static const unsigned s_refCountFlagIsIdentifier = 0x4; + static const unsigned s_refCountMaskBufferOwnership = 0x3; + + UStringImpl* bufferOwnerString() { return (bufferOwnership() == BufferSubstring) ? m_bufferSubstring : this; } + const UStringImpl* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring) ? m_bufferSubstring : this; } SharedUChar* baseSharedBuffer(); - unsigned bufferOwnership() const { return m_dataBuffer & s_bufferOwnershipMask; } - bool isStatic() const { return m_refCount & 1; } + unsigned bufferOwnership() const { return m_refCountAndFlags & s_refCountMaskBufferOwnership; } + bool isStatic() const { return m_refCountAndFlags & s_refCountFlagStatic; } // unshared data UChar* m_data; + union { + void* m_buffer; + UStringImpl* m_bufferSubstring; + SharedUChar* m_bufferShared; + }; int m_length; - unsigned m_refCount; - mutable unsigned m_hash : 31; - mutable unsigned m_isIdentifier : 1; - UntypedPtrAndBitfield m_dataBuffer; + unsigned m_refCountAndFlags; + mutable unsigned m_hash; JS_EXPORTDATA static UStringImpl* s_null; JS_EXPORTDATA static UStringImpl* s_empty; diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/BlockAllocatorSymbian.cpp b/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/BlockAllocatorSymbian.cpp new file mode 100644 index 0000000..6a28e9e --- /dev/null +++ b/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/BlockAllocatorSymbian.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if OS(SYMBIAN) + +#include "BlockAllocatorSymbian.h" + + +namespace WTF { + +/** Efficiently allocates blocks of size blockSize with blockSize alignment. + * Primarly designed for JSC Collector's needs. + * Not thread-safe. + */ +AlignedBlockAllocator::AlignedBlockAllocator(TUint32 reservationSize, TUint32 blockSize ) + : m_reservation(reservationSize), + m_blockSize(blockSize) +{ + + // Get system's page size value. + SYMBIAN_PAGESIZE(m_pageSize); + + // We only accept multiples of system page size for both initial reservation and the alignment/block size + m_reservation = SYMBIAN_ROUNDUPTOMULTIPLE(m_reservation, m_pageSize); + __ASSERT_ALWAYS(SYMBIAN_ROUNDUPTOMULTIPLE(m_blockSize, m_pageSize), User::Panic(_L("AlignedBlockAllocator1"), KErrArgument)); + + // Calculate max. bit flags we need to carve a reservationSize range into blockSize-sized blocks + m_map.numBits = m_reservation / m_blockSize; + const TUint32 bitsPerWord = 8*sizeof(TUint32); + const TUint32 numWords = (m_map.numBits + bitsPerWord -1) / bitsPerWord; + + m_map.bits = new TUint32[numWords]; + __ASSERT_ALWAYS(m_map.bits, User::Panic(_L("AlignedBlockAllocator2"), KErrNoMemory)); + m_map.clearAll(); + + // Open a Symbian RChunk, and reserve requested virtual address range + // Any thread in this process can operate this rchunk due to EOwnerProcess access rights. + TInt ret = m_chunk.CreateDisconnectedLocal(0 , 0, (TInt)m_reservation , EOwnerProcess); + if (ret != KErrNone) + User::Panic(_L("AlignedBlockAllocator3"), ret); + + // This is the offset to m_chunk.Base() required to make it m_blockSize-aligned + m_offset = SYMBIAN_ROUNDUPTOMULTIPLE(TUint32(m_chunk.Base()), m_blockSize) - TUint(m_chunk.Base()); + +} + +void* AlignedBlockAllocator::alloc() +{ + + TInt freeRam = 0; + void* address = 0; + + // Look up first free slot in bit map + const TInt freeIdx = m_map.findFree(); + + // Pseudo OOM: We ate up the address space we reserved.. + // ..even though the device may have free RAM left + if (freeIdx < 0) + return 0; + + TInt ret = m_chunk.Commit(m_offset + (m_blockSize * freeIdx), m_blockSize); + if (ret != KErrNone) + return 0; // True OOM: Device didn't have physical RAM to spare + + // Updated bit to mark region as in use. + m_map.set(freeIdx); + + // Calculate address of committed region (block) + address = (void*)( (m_chunk.Base() + m_offset) + (TUint)(m_blockSize * freeIdx) ); + + return address; +} + +void AlignedBlockAllocator::free(void* block) +{ + // Calculate index of block to be freed + TInt idx = TUint(static_cast<TUint8*>(block) - m_chunk.Base() - m_offset) / m_blockSize; + + __ASSERT_DEBUG(idx >= 0 && idx < m_map.numBits, User::Panic(_L("AlignedBlockAllocator4"), KErrCorrupt)); // valid index check + __ASSERT_DEBUG(m_map.get(idx), User::Panic(_L("AlignedBlockAllocator5"), KErrCorrupt)); // in-use flag check + + // Return committed region to system RAM pool (the physical RAM becomes usable by others) + TInt ret = m_chunk.Decommit(m_offset + m_blockSize * idx, m_blockSize); + + // mark this available again + m_map.clear(idx); +} + +void AlignedBlockAllocator::destroy() +{ + // release everything! + m_chunk.Decommit(0, m_chunk.MaxSize()); + m_map.clearAll(); +} + +AlignedBlockAllocator::~AlignedBlockAllocator() +{ + destroy(); + m_chunk.Close(); + delete [] m_map.bits; +} + +} // end of namespace + +#endif // SYMBIAN diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/BlockAllocatorSymbian.h b/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/BlockAllocatorSymbian.h new file mode 100644 index 0000000..21422f6 --- /dev/null +++ b/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/BlockAllocatorSymbian.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BlockAllocatorSymbian_h +#define BlockAllocatorSymbian_h + +#include <e32cmn.h> +#include <e32std.h> +#include <hal.h> + + +#define SYMBIAN_PAGESIZE(x) (HAL::Get(HALData::EMemoryPageSize, x)); +#define SYMBIAN_FREERAM(x) (HAL::Get(HALData::EMemoryRAMFree, x)); +#define SYMBIAN_ROUNDUPTOMULTIPLE(x, multipleof) ( (x + multipleof - 1) & ~(multipleof - 1) ) + +// Set sane defaults if -D<flagname=value> wasn't provided via compiler args +#ifndef JSCCOLLECTOR_VIRTUALMEM_RESERVATION +#if defined(__WINS__) + // Emulator has limited virtual address space + #define JSCCOLLECTOR_VIRTUALMEM_RESERVATION (4*1024*1024) +#else + // HW has plenty of virtual addresses + #define JSCCOLLECTOR_VIRTUALMEM_RESERVATION (128*1024*1024) +#endif +#endif + +namespace WTF { + +/** + * Allocates contiguous region of size blockSize with blockSize-aligned address. + * blockSize must be a multiple of system page size (typically 4K on Symbian/ARM) + * + * @param reservationSize Virtual address range to be reserved upon creation of chunk (bytes). + * @param blockSize Size of a single allocation. Returned address will also be blockSize-aligned. + */ +class AlignedBlockAllocator { + public: + AlignedBlockAllocator(TUint32 reservationSize, TUint32 blockSize); + ~AlignedBlockAllocator(); + void destroy(); + void* alloc(); + void free(void* data); + + private: + RChunk m_chunk; // Symbian chunk that lets us reserve/commit/decommit + TUint m_offset; // offset of first committed region from base + TInt m_pageSize; // cached value of system page size, typically 4K on Symbian + TUint32 m_reservation; + TUint32 m_blockSize; + + // Tracks comitted/decommitted state of a blockSize region + struct { + + TUint32 *bits; // array of bit flags + TUint32 numBits; // number of regions to keep track of + + bool get(TUint32 n) const + { + return !!(bits[n >> 5] & (1 << (n & 0x1F))); + } + + void set(TUint32 n) + { + bits[n >> 5] |= (1 << (n & 0x1F)); + } + + void clear(TUint32 n) + { + bits[n >> 5] &= ~(1 << (n & 0x1F)); + } + + void clearAll() + { + for (TUint32 i = 0; i < numBits; i++) + clear(i); + } + + TInt findFree() const + { + for (TUint32 i = 0; i < numBits; i++) { + if (!get(i)) + return i; + } + return -1; + } + + } m_map; + +}; + +} + +#endif // end of BlockAllocatorSymbian_h + + diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/RegisterFileAllocatorSymbian.cpp b/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/RegisterFileAllocatorSymbian.cpp index da5cc99..e89dd7a 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/RegisterFileAllocatorSymbian.cpp +++ b/src/3rdparty/javascriptcore/JavaScriptCore/wtf/symbian/RegisterFileAllocatorSymbian.cpp @@ -83,10 +83,16 @@ void RegisterFileAllocator::grow(void* newEnd) TInt nBytes = (TInt)(newEnd) - (TInt)(m_comEnd); nBytes = SYMBIAN_ROUNDUPTOMULTIPLE(nBytes, m_poolSize); TInt offset = (TInt)m_comEnd - (TInt)m_buffer; + // The reserved size is not guaranteed to be a multiple of the pool size. + TInt maxBytes = (TInt)m_resEnd - (TInt)m_comEnd; + if (nBytes > maxBytes) + nBytes = maxBytes; TInt ret = m_chunk.Commit(offset, nBytes); if (ret == KErrNone) m_comEnd = (void*)(m_chunk.Base() + m_chunk.Size()); + else + CRASH(); } } diff --git a/src/3rdparty/javascriptcore/VERSION b/src/3rdparty/javascriptcore/VERSION index 9991ac0..13943b2 100644 --- a/src/3rdparty/javascriptcore/VERSION +++ b/src/3rdparty/javascriptcore/VERSION @@ -4,8 +4,8 @@ This is a snapshot of JavaScriptCore from The commit imported was from the - javascriptcore-snapshot-05102010 branch/tag + javascriptcore-snapshot-27012011 branch/tag and has the sha1 checksum - 82ead85cfea5859044eeb25b33314dcc0fa5eea1 + 3ab0f621048fbeb480b687a28ed31d92d8506150 diff --git a/src/3rdparty/phonon/phonon/factory.cpp b/src/3rdparty/phonon/phonon/factory.cpp index 2785dff..b5e565b 100644 --- a/src/3rdparty/phonon/phonon/factory.cpp +++ b/src/3rdparty/phonon/phonon/factory.cpp @@ -137,21 +137,15 @@ bool FactoryPrivate::createBackend() QStringList plugins(dir.entryList(QDir::Files)); #ifdef Q_OS_SYMBIAN - /* On Symbian OS we might have two plugins, one which uses Symbian - * MMF framework("mmf"), and one which uses Real Networks's - * Helix("hxphonon"). We prefer the latter because it's more - * sophisticated, so we make sure the Helix backend is attempted - * to be loaded first, and the MMF backend is used for backup. */ - { - const int helix = plugins.indexOf(QLatin1String("hxphonon")); - if (helix != -1) - plugins.move(helix, 0); - } + static const QString preferredPluginName = QLatin1String("phonon_mmf"); + const int preferredPluginIndex = plugins.indexOf(preferredPluginName + ".qtplugin"); + if (preferredPluginIndex != -1) + plugins.move(preferredPluginIndex, 0); #endif const QStringList files = dir.entryList(QDir::Files); - for (int i = 0; i < files.count(); ++i) { - QPluginLoader pluginLoader(libPath + files.at(i)); + for (int i = 0; i < plugins.count(); ++i) { + QPluginLoader pluginLoader(libPath + plugins.at(i)); if (!pluginLoader.load()) { pDebug() << Q_FUNC_INFO << " load failed:" << pluginLoader.errorString(); diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index 212e85d..c76cb89 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -431,12 +431,17 @@ void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator fun { // will override any existing interpolators QInterpolatorVector *interpolators = registeredInterpolators(); + // When built on solaris with GCC, the destructors can be called + // in such an order that we get here with interpolators == NULL, + // to continue causes the app to crash on exit with a SEGV + if (interpolators) { #ifndef QT_NO_THREAD - QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); + QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); #endif - if (int(interpolationType) >= interpolators->count()) - interpolators->resize(int(interpolationType) + 1); - interpolators->replace(interpolationType, func); + if (int(interpolationType) >= interpolators->count()) + interpolators->resize(int(interpolationType) + 1); + interpolators->replace(interpolationType, func); + } } diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index d541f36..bd9d554 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -511,9 +511,16 @@ public: #if 0 // these values are reserved for Maemo5 - do not re-use them WA_Maemo5NonComposited = 126, WA_Maemo5StackedWindow = 127, - WA_Maemo5PortraitOrientation = 128, - WA_Maemo5LandscapeOrientation = 129, - WA_Maemo5AutoOrientation = 130, +#endif + + WA_LockPortraitOrientation = 128, + WA_LockLandscapeOrientation = 129, + WA_AutoOrientation = 130, + +#if 0 // these values are reserved for Maemo5 - do not re-use them + WA_Maemo5PortraitOrientation = WA_LockPortraitOrientation, + WA_Maemo5LandscapeOrientation = WA_LockLandscapeOrientation, + WA_Maemo5AutoOrientation = WA_AutoOrientation, WA_Maemo5ShowProgressIndicator = 131, #endif diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index b120f7f..f9f5362 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -169,27 +169,17 @@ private: Q_GLOBAL_STATIC(QMutex, processManagerGlobalMutex) -static QProcessManager *processManagerInstance = 0; - -static QProcessManager *processManager() -{ +static QProcessManager *processManager() { // The constructor of QProcessManager should be called only once // so we cannot use Q_GLOBAL_STATIC directly for QProcessManager QMutex *mutex = processManagerGlobalMutex(); QMutexLocker locker(mutex); - - if (!processManagerInstance) - QProcessPrivate::initializeProcessManager(); - - Q_ASSERT(processManagerInstance); - return processManagerInstance; + static QProcessManager processManager; + return &processManager; } QProcessManager::QProcessManager() { - // can only be called from main thread - Q_ASSERT(!qApp || qApp->thread() == QThread::currentThread()); - #if defined (QPROCESS_DEBUG) qDebug() << "QProcessManager::QProcessManager()"; #endif @@ -208,8 +198,6 @@ QProcessManager::QProcessManager() ::sigaction(SIGCHLD, &action, &oldAction); if (oldAction.sa_handler != qt_sa_sigchld_handler) qt_sa_old_sigchld_handler = oldAction.sa_handler; - - processManagerInstance = this; } QProcessManager::~QProcessManager() @@ -238,8 +226,6 @@ QProcessManager::~QProcessManager() if (oldAction.sa_handler != qt_sa_sigchld_handler) { ::sigaction(SIGCHLD, &oldAction, 0); } - - processManagerInstance = 0; } void QProcessManager::run() @@ -1306,15 +1292,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a void QProcessPrivate::initializeProcessManager() { - if (qApp && qApp->thread() != QThread::currentThread()) { - // The process manager must be initialized in the main thread - // Note: The call below will re-enter this function, but in the right thread, - // so the else statement below will be executed. - QMetaObject::invokeMethod(qApp, "_q_initializeProcessManager", Qt::BlockingQueuedConnection); - } else { - static QProcessManager processManager; - Q_UNUSED(processManager); - } + (void) processManager(); } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 9bd32d1..26a398e 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -392,16 +392,6 @@ void QCoreApplicationPrivate::createEventDispatcher() #endif } -void QCoreApplicationPrivate::_q_initializeProcessManager() -{ -#ifndef QT_NO_PROCESS -# ifdef Q_OS_UNIX - QProcessPrivate::initializeProcessManager(); -# endif -#endif -} - - QThread *QCoreApplicationPrivate::theMainThread = 0; QThread *QCoreApplicationPrivate::mainThread() { @@ -666,6 +656,12 @@ void QCoreApplication::init() } #endif +#if defined(Q_OS_UNIX) && !(defined(QT_NO_PROCESS)) + // Make sure the process manager thread object is created in the main + // thread. + QProcessPrivate::initializeProcessManager(); +#endif + #ifdef QT_EVAL extern void qt_core_eval_init(uint); qt_core_eval_init(d->application_type); @@ -2732,5 +2728,3 @@ int QCoreApplication::loopLevel() */ QT_END_NAMESPACE - -#include "moc_qcoreapplication.cpp" diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index 024c509..3957158 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -205,7 +205,6 @@ protected: QCoreApplication(QCoreApplicationPrivate &p); private: - Q_PRIVATE_SLOT(d_func(), void _q_initializeProcessManager()) static bool sendSpontaneousEvent(QObject *receiver, QEvent *event); bool notifyInternal(QObject *receiver, QEvent *event); diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index fdceab4..add2a35 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -85,8 +85,6 @@ public: bool sendThroughObjectEventFilters(QObject *, QEvent *); bool notify_helper(QObject *, QEvent *); - void _q_initializeProcessManager(); - virtual QString appName() const; virtual void createEventDispatcher(); static void removePostedEvent(QEvent *); diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp index 46604b0..25c02c4 100644 --- a/src/corelib/kernel/qeventdispatcher_unix.cpp +++ b/src/corelib/kernel/qeventdispatcher_unix.cpp @@ -509,7 +509,6 @@ bool QTimerInfoList::unregisterTimer(int timerId) } } // id not found - qWarning("Application asked to unregister timer 0x%x which is not registered in this thread. Fix application.", timerId); return false; } diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp index b61509e..78715a1 100644 --- a/src/corelib/kernel/qsharedmemory.cpp +++ b/src/corelib/kernel/qsharedmemory.cpp @@ -458,11 +458,7 @@ bool QSharedMemory::detach() return false; #endif - if (d->detach()) { - d->size = 0; - return true; - } - return false; + return d->detach(); } /*! diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp index fc0a438..b30117f 100644 --- a/src/corelib/kernel/qsharedmemory_unix.cpp +++ b/src/corelib/kernel/qsharedmemory_unix.cpp @@ -198,7 +198,7 @@ bool QSharedMemoryPrivate::create(int size) } // create - if (-1 == shmget(handle(), size, 0666 | IPC_CREAT | IPC_EXCL)) { + if (-1 == shmget(unix_key, size, 0666 | IPC_CREAT | IPC_EXCL)) { QString function = QLatin1String("QSharedMemory::create"); switch (errno) { case EINVAL: @@ -219,10 +219,7 @@ bool QSharedMemoryPrivate::create(int size) bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode) { // grab the shared memory segment id - if (!handle()) - return false; - - int id = shmget(handle(), 0, (mode == QSharedMemory::ReadOnly ? 0444 : 0660)); + int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0444 : 0660)); if (-1 == id) { setErrorString(QLatin1String("QSharedMemory::attach (shmget)")); return false; @@ -264,12 +261,11 @@ bool QSharedMemoryPrivate::detach() return false; } memory = 0; + size = 0; // Get the number of current attachments - if (!handle()) - return false; - int id = shmget(handle(), 0, 0444); - unix_key = 0; + int id = shmget(unix_key, 0, 0444); + cleanHandle(); struct shmid_ds shmid_ds; if (0 != shmctl(id, IPC_STAT, &shmid_ds)) { diff --git a/src/corelib/kernel/qsharedmemory_win.cpp b/src/corelib/kernel/qsharedmemory_win.cpp index 75ab4e8..bc13e33 100644 --- a/src/corelib/kernel/qsharedmemory_win.cpp +++ b/src/corelib/kernel/qsharedmemory_win.cpp @@ -123,8 +123,8 @@ bool QSharedMemoryPrivate::cleanHandle() { if (hand != 0 && !CloseHandle(hand)) { hand = 0; - return false; setErrorString(QLatin1String("QSharedMemory::cleanHandle")); + return false; } hand = 0; return true; @@ -183,6 +183,7 @@ bool QSharedMemoryPrivate::detach() return false; } memory = 0; + size = 0; // close handle return cleanHandle(); diff --git a/src/corelib/kernel/qsystemsemaphore_symbian.cpp b/src/corelib/kernel/qsystemsemaphore_symbian.cpp index 07cfffc..0d257b8 100644 --- a/src/corelib/kernel/qsystemsemaphore_symbian.cpp +++ b/src/corelib/kernel/qsystemsemaphore_symbian.cpp @@ -73,6 +73,7 @@ void QSystemSemaphorePrivate::setErrorString(const QString &function, int err) case KErrInUse: errorString = QCoreApplication::tr("%1: out of resources", "QSystemSemaphore").arg(function); error = QSystemSemaphore::OutOfResources; + break; case KErrPermissionDenied: errorString = QCoreApplication::tr("%1: permission denied", "QSystemSemaphore").arg(function); error = QSystemSemaphore::PermissionDenied; @@ -97,8 +98,7 @@ int QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode) if (key.isEmpty()) return 0; - QString safeName = makeKeyFileName(); - TPtrC name(qt_QString2TPtrC(safeName)); + TPtrC name(qt_QString2TPtrC(fileName)); int err = KErrAlreadyExists; int tryCount = 10; // Sort out race conditions by retrying several times until existing handle is acquired. diff --git a/src/corelib/kernel/qsystemsemaphore_win.cpp b/src/corelib/kernel/qsystemsemaphore_win.cpp index 7f002f8..fad50f2 100644 --- a/src/corelib/kernel/qsystemsemaphore_win.cpp +++ b/src/corelib/kernel/qsystemsemaphore_win.cpp @@ -86,8 +86,7 @@ HANDLE QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode) // Create it if it doesn't already exists. if (semaphore == 0) { - QString safeName = makeKeyFileName(); - semaphore = CreateSemaphore(0, initialValue, MAXLONG, (wchar_t*)safeName.utf16()); + semaphore = CreateSemaphore(0, initialValue, MAXLONG, (wchar_t*)fileName.utf16()); if (semaphore == NULL) setErrorString(QLatin1String("QSystemSemaphore::handle")); } diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 392c45d..b2af5ca 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1245,9 +1245,7 @@ void QStateMachinePrivate::_q_process() #endif while (processing) { if (stop) { - stop = false; processing = false; - stopProcessingReason = Stopped; break; } QSet<QAbstractTransition*> enabledTransitions; @@ -1299,6 +1297,11 @@ void QStateMachinePrivate::_q_process() #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": finished the event processing loop"; #endif + if (stop) { + stop = false; + stopProcessingReason = Stopped; + } + switch (stopProcessingReason) { case EventQueueEmpty: break; diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp index 5321252..11e2060 100644 --- a/src/corelib/thread/qmutex_unix.cpp +++ b/src/corelib/thread/qmutex_unix.cpp @@ -180,8 +180,11 @@ bool QMutexPrivate::wait(int timeout) errorCode = pthread_cond_timedwait(&cond, &mutex, &ti); } if (errorCode) { - if (errorCode == ETIMEDOUT) + if (errorCode == ETIMEDOUT) { + if (wakeup) + errorCode = 0; break; + } report_error(errorCode, "QMutex::lock()", "cv wait"); } } diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 1494745..326f494 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -515,10 +515,12 @@ int QThread::exec() Note that unlike the C library function of the same name, this function \e does return to the caller -- it is event processing - that stops. - - This function does nothing if the thread does not have an event - loop. + that stops. + + No QEventLoops will be started anymore in this thread until + QThread::exec() has been called again. If the eventloop in QThread::exec() + is not running then the next call to QThread::exec() will also return + immediately. \sa quit() QEventLoop */ diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 273c5bd..b29e5b6 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -44,7 +44,17 @@ #ifndef QSHAREDPOINTER_H #error Do not include qsharedpointer_impl.h directly #endif + #if 0 +// These macros are duplicated here to make syncqt not complain a about +// this header, as we have a "qt_sync_stop_processing" below, which in turn +// is here because this file contains a template mess and duplicates the +// classes found in qsharedpointer.h +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE +QT_MODULE(Core) +QT_END_NAMESPACE +QT_END_HEADER #pragma qt_sync_stop_processing #endif diff --git a/src/dbus/qdbusargument.h b/src/dbus/qdbusargument.h index 2c8693b..e331d8f 100644 --- a/src/dbus/qdbusargument.h +++ b/src/dbus/qdbusargument.h @@ -376,6 +376,21 @@ inline const QDBusArgument &operator>>(const QDBusArgument &arg, QHash<Key, T> & return arg; } +inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantHash &map) +{ + arg.beginMap(QVariant::String, qMetaTypeId<QDBusVariant>()); + QVariantHash::ConstIterator it = map.constBegin(); + QVariantHash::ConstIterator end = map.constEnd(); + for ( ; it != end; ++it) { + arg.beginMapEntry(); + arg << it.key() << QDBusVariant(it.value()); + arg.endMapEntry(); + } + arg.endMap(); + return arg; +} + + QT_END_NAMESPACE Q_DECLARE_METATYPE(QDBusArgument) diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index eb312d2..3fb63eb 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -632,10 +632,26 @@ bool QDBusConnection::connect(const QString &service, const QString &path, const if (!receiver || !slot || !d || !d->connection) return false; - if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) - return false; if (interface.isEmpty() && name.isEmpty()) return false; + if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) { +#ifndef QT_NO_DEBUG + qWarning("QDBusConnection::connect: interface name '%s' is not valid", interface.toLatin1().constData()); +#endif + return false; + } + if (!service.isEmpty() && !QDBusUtil::isValidBusName(service)) { +#ifndef QT_NO_DEBUG + qWarning("QDBusConnection::connect: service name '%s' is not valid", service.toLatin1().constData()); +#endif + return false; + } + if (!path.isEmpty() && !QDBusUtil::isValidObjectPath(path)) { +#ifndef QT_NO_DEBUG + qWarning("QDBusConnection::connect: object path '%s' is not valid", path.toLatin1().constData()); +#endif + return false; + } QDBusWriteLocker locker(ConnectAction, d); return d->connectSignal(service, path, interface, name, argumentMatch, signature, receiver, slot); diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index 3c0cb6e..1358524 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -558,8 +558,9 @@ bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) switch (amsg.type()) { case QDBusMessage::SignalMessage: handleSignal(amsg); - return true; - break; + // if there are any other filters in this DBusConnection, + // let them see the signal too + return false; case QDBusMessage::MethodCallMessage: handleObjectCall(amsg); return true; diff --git a/src/dbus/qdbusmetatype.cpp b/src/dbus/qdbusmetatype.cpp index 6de5d93..9f29205 100644 --- a/src/dbus/qdbusmetatype.cpp +++ b/src/dbus/qdbusmetatype.cpp @@ -127,6 +127,7 @@ void QDBusMetaTypeId::init() registerHelper<QLineF>(); registerHelper<QVariantList>(); registerHelper<QVariantMap>(); + registerHelper<QVariantHash>(); qDBusRegisterMetaType<QList<bool> >(); qDBusRegisterMetaType<QList<short> >(); diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro index 4ed4f9f..6bd314f 100644 --- a/src/declarative/declarative.pro +++ b/src/declarative/declarative.pro @@ -27,6 +27,13 @@ include(debugger/debugger.pri) symbian: { TARGET.UID3=0x2001E623 LIBS += -lefsrv + + contains(QT_CONFIG, freetype) { + DEFINES += QT_NO_FONTCONFIG + INCLUDEPATH += \ + ../3rdparty/freetype/src \ + ../3rdparty/freetype/include + } } DEFINES += QT_NO_OPENTYPE diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp index db63cda..d35718b 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable.cpp +++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp @@ -299,7 +299,7 @@ void QDeclarativeFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4); timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4); } else { - timeline.set(data.move, minExtent); + timeline.set(data.move, maxExtent); } } vTime = timeline.time(); diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index 89d7493..4c04a6b 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview.cpp +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -2165,6 +2165,7 @@ void QDeclarativeGridView::positionViewAtIndex(int index, int mode) if (d->layoutScheduled) d->layout(); qreal pos = d->position(); + qreal maxExtent = d->flow == QDeclarativeGridView::LeftToRight ? -maxYExtent() : -maxXExtent(); FxGridItem *item = d->visibleItem(index); if (!item) { int itemPos = d->rowPosAt(index); @@ -2172,7 +2173,7 @@ void QDeclarativeGridView::positionViewAtIndex(int index, int mode) QList<FxGridItem*> oldVisible = d->visibleItems; d->visibleItems.clear(); d->visibleIndex = index - index % d->columns; - d->setPosition(itemPos); + d->setPosition(qMin(qreal(itemPos), maxExtent)); // now release the reference to all the old visible items. for (int i = 0; i < oldVisible.count(); ++i) d->releaseItem(oldVisible.at(i)); @@ -2202,7 +2203,6 @@ void QDeclarativeGridView::positionViewAtIndex(int index, int mode) if (itemPos < pos) pos = itemPos; } - qreal maxExtent = d->flow == QDeclarativeGridView::LeftToRight ? -maxYExtent() : -maxXExtent(); pos = qMin(pos, maxExtent); qreal minExtent = d->flow == QDeclarativeGridView::LeftToRight ? -minYExtent() : -minXExtent(); pos = qMax(pos, minExtent); diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp index b4fd571..b4b3fa7 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview.cpp +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -2603,6 +2603,7 @@ void QDeclarativeListView::positionViewAtIndex(int index, int mode) d->layout(); qreal pos = d->position(); FxListItem *item = d->visibleItem(index); + qreal maxExtent = d->orient == QDeclarativeListView::Vertical ? -maxYExtent() : -maxXExtent(); if (!item) { int itemPos = d->positionAt(index); // save the currently visible items in case any of them end up visible again @@ -2610,7 +2611,7 @@ void QDeclarativeListView::positionViewAtIndex(int index, int mode) d->visibleItems.clear(); d->visiblePos = itemPos; d->visibleIndex = index; - d->setPosition(itemPos); + d->setPosition(qMin(qreal(itemPos), maxExtent)); // now release the reference to all the old visible items. for (int i = 0; i < oldVisible.count(); ++i) d->releaseItem(oldVisible.at(i)); @@ -2640,7 +2641,6 @@ void QDeclarativeListView::positionViewAtIndex(int index, int mode) if (itemPos < pos) pos = itemPos; } - qreal maxExtent = d->orient == QDeclarativeListView::Vertical ? -maxYExtent() : -maxXExtent(); pos = qMin(pos, maxExtent); qreal minExtent = d->orient == QDeclarativeListView::Vertical ? -minYExtent() : -minXExtent(); pos = qMax(pos, minExtent); diff --git a/src/declarative/graphicsitems/qdeclarativeloader.cpp b/src/declarative/graphicsitems/qdeclarativeloader.cpp index 86e438f..ded2be3 100644 --- a/src/declarative/graphicsitems/qdeclarativeloader.cpp +++ b/src/declarative/graphicsitems/qdeclarativeloader.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE QDeclarativeLoaderPrivate::QDeclarativeLoaderPrivate() - : item(0), component(0), ownComponent(false), isComponentComplete(false) + : item(0), component(0), ownComponent(false) { } @@ -262,7 +262,6 @@ void QDeclarativeLoader::setSource(const QUrl &url) d->clear(); d->source = url; - if (d->source.isEmpty()) { emit sourceChanged(); emit statusChanged(); @@ -273,9 +272,18 @@ void QDeclarativeLoader::setSource(const QUrl &url) d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this); d->ownComponent = true; - - if (d->isComponentComplete) - d->load(); + if (!d->component->isLoading()) { + d->_q_sourceLoaded(); + } else { + connect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), + this, SLOT(_q_sourceLoaded())); + connect(d->component, SIGNAL(progressChanged(qreal)), + this, SIGNAL(progressChanged())); + emit statusChanged(); + emit progressChanged(); + emit sourceChanged(); + emit itemChanged(); + } } /*! @@ -316,7 +324,6 @@ void QDeclarativeLoader::setSourceComponent(QDeclarativeComponent *comp) d->component = comp; d->ownComponent = false; - if (!d->component) { emit sourceChanged(); emit statusChanged(); @@ -325,8 +332,18 @@ void QDeclarativeLoader::setSourceComponent(QDeclarativeComponent *comp) return; } - if (d->isComponentComplete) - d->load(); + if (!d->component->isLoading()) { + d->_q_sourceLoaded(); + } else { + connect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), + this, SLOT(_q_sourceLoaded())); + connect(d->component, SIGNAL(progressChanged(qreal)), + this, SIGNAL(progressChanged())); + emit progressChanged(); + emit sourceChanged(); + emit statusChanged(); + emit itemChanged(); + } } void QDeclarativeLoader::resetSourceComponent() @@ -334,27 +351,6 @@ void QDeclarativeLoader::resetSourceComponent() setSourceComponent(0); } -void QDeclarativeLoaderPrivate::load() -{ - Q_Q(QDeclarativeLoader); - - if (!isComponentComplete || !component) - return; - - if (!component->isLoading()) { - _q_sourceLoaded(); - } else { - QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), - q, SLOT(_q_sourceLoaded())); - QObject::connect(component, SIGNAL(progressChanged(qreal)), - q, SIGNAL(progressChanged())); - emit q->statusChanged(); - emit q->progressChanged(); - emit q->sourceChanged(); - emit q->itemChanged(); - } -} - void QDeclarativeLoaderPrivate::_q_sourceLoaded() { Q_Q(QDeclarativeLoader); @@ -469,11 +465,9 @@ QDeclarativeLoader::Status QDeclarativeLoader::status() const void QDeclarativeLoader::componentComplete() { - Q_D(QDeclarativeLoader); - QDeclarativeItem::componentComplete(); - d->isComponentComplete = true; - d->load(); + if (status() == Ready) + emit loaded(); } diff --git a/src/declarative/graphicsitems/qdeclarativeloader_p_p.h b/src/declarative/graphicsitems/qdeclarativeloader_p_p.h index 81ca66d..45ab595 100644 --- a/src/declarative/graphicsitems/qdeclarativeloader_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativeloader_p_p.h @@ -72,13 +72,11 @@ public: void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); void clear(); void initResize(); - void load(); QUrl source; QGraphicsObject *item; QDeclarativeComponent *component; bool ownComponent : 1; - bool isComponentComplete : 1; void _q_sourceLoaded(); void _q_updateSize(bool loaderGeometryChanged = true); diff --git a/src/declarative/graphicsitems/qdeclarativescalegrid.cpp b/src/declarative/graphicsitems/qdeclarativescalegrid.cpp index 804e91d..3ad0da6 100644 --- a/src/declarative/graphicsitems/qdeclarativescalegrid.cpp +++ b/src/declarative/graphicsitems/qdeclarativescalegrid.cpp @@ -136,12 +136,12 @@ QDeclarativeGridScaledImage::QDeclarativeGridScaledImage(QIODevice *data) if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) continue; - QStringList list = line.split(QLatin1Char(':')); - if (list.count() != 2) + int colonId = line.indexOf(QLatin1Char(':')); + if (colonId <= 0) return; - - list[0] = list[0].trimmed(); - list[1] = list[1].trimmed(); + QStringList list; + list.append(line.left(colonId).trimmed()); + list.append(line.mid(colonId+1).trimmed()); if (list[0] == QLatin1String("border.left")) l = list[1].toInt(); diff --git a/src/declarative/graphicsitems/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp index 2f3c8e5..640965c 100644 --- a/src/declarative/graphicsitems/qdeclarativetext.cpp +++ b/src/declarative/graphicsitems/qdeclarativetext.cpp @@ -736,19 +736,28 @@ QDeclarativeText::~QDeclarativeText() QFont QDeclarativeText::font() const { Q_D(const QDeclarativeText); - return d->font; + return d->sourceFont; } void QDeclarativeText::setFont(const QFont &font) { Q_D(QDeclarativeText); - if (d->font == font) + if (d->sourceFont == font) return; + d->sourceFont = font; + QFont oldFont = d->font; d->font = font; - d->updateLayout(); + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } + + if (oldFont != d->font) + d->updateLayout(); - emit fontChanged(d->font); + emit fontChanged(d->sourceFont); } /*! diff --git a/src/declarative/graphicsitems/qdeclarativetext_p_p.h b/src/declarative/graphicsitems/qdeclarativetext_p_p.h index 67f2289..e749bc9 100644 --- a/src/declarative/graphicsitems/qdeclarativetext_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetext_p_p.h @@ -79,6 +79,7 @@ public: QString text; QFont font; + QFont sourceFont; QColor color; QDeclarativeText::TextStyle style; QColor styleColor; diff --git a/src/declarative/graphicsitems/qdeclarativetextedit.cpp b/src/declarative/graphicsitems/qdeclarativetextedit.cpp index 675f8d9..cb62a2c 100644 --- a/src/declarative/graphicsitems/qdeclarativetextedit.cpp +++ b/src/declarative/graphicsitems/qdeclarativetextedit.cpp @@ -326,22 +326,35 @@ void QDeclarativeTextEdit::setTextFormat(TextFormat format) QFont QDeclarativeTextEdit::font() const { Q_D(const QDeclarativeTextEdit); - return d->font; + return d->sourceFont; } void QDeclarativeTextEdit::setFont(const QFont &font) { Q_D(QDeclarativeTextEdit); + if (d->sourceFont == font) + return; + + d->sourceFont = font; + QFont oldFont = d->font; d->font = font; + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } - clearCache(); - d->document->setDefaultFont(d->font); - if(d->cursor){ - d->cursor->setHeight(QFontMetrics(d->font).height()); - moveCursorDelegate(); + if (oldFont != d->font) { + clearCache(); + d->document->setDefaultFont(d->font); + if(d->cursor){ + d->cursor->setHeight(QFontMetrics(d->font).height()); + moveCursorDelegate(); + } + updateSize(); + update(); } - updateSize(); - update(); + emit fontChanged(d->sourceFont); } /*! @@ -1352,8 +1365,12 @@ void QDeclarativeTextEdit::updateSize() int dy = height(); // ### assumes that if the width is set, the text will fill to edges // ### (unless wrap is false, then clipping will occur) - if (widthValid() && d->document->textWidth() != width()) - d->document->setTextWidth(width()); + if (widthValid()) { + if (d->document->textWidth() != width()) + d->document->setTextWidth(width()); + } else { + d->document->setTextWidth(-1); + } dy -= (int)d->document->size().height(); int nyoff; diff --git a/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h b/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h index 68a9cc8..3a45541 100644 --- a/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h @@ -92,6 +92,7 @@ public: QString text; QFont font; + QFont sourceFont; QColor color; QColor selectionColor; QColor selectedTextColor; diff --git a/src/declarative/graphicsitems/qdeclarativetextinput.cpp b/src/declarative/graphicsitems/qdeclarativetextinput.cpp index 57a2177..ef3404f 100644 --- a/src/declarative/graphicsitems/qdeclarativetextinput.cpp +++ b/src/declarative/graphicsitems/qdeclarativetextinput.cpp @@ -213,24 +213,33 @@ void QDeclarativeTextInput::setText(const QString &s) QFont QDeclarativeTextInput::font() const { Q_D(const QDeclarativeTextInput); - return d->font; + return d->sourceFont; } void QDeclarativeTextInput::setFont(const QFont &font) { Q_D(QDeclarativeTextInput); - if (d->font == font) + if (d->sourceFont == font) return; + d->sourceFont = font; + QFont oldFont = d->font; d->font = font; + if (d->font.pointSizeF() != -1) { + // 0.5pt resolution + qreal size = qRound(d->font.pointSizeF()*2.0); + d->font.setPointSizeF(size/2.0); + } - d->control->setFont(d->font); - if(d->cursorItem){ - d->cursorItem->setHeight(QFontMetrics(d->font).height()); - moveCursor(); + if (oldFont != d->font) { + d->control->setFont(d->font); + if(d->cursorItem){ + d->cursorItem->setHeight(QFontMetrics(d->font).height()); + moveCursor(); + } + updateSize(); } - updateSize(); - emit fontChanged(d->font); + emit fontChanged(d->sourceFont); } /*! diff --git a/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h b/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h index 18e9c81..16827b3 100644 --- a/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h @@ -107,6 +107,7 @@ public: QLineControl* control; QFont font; + QFont sourceFont; QColor color; QColor selectionColor; QColor selectedTextColor; diff --git a/src/declarative/qml/parser/qdeclarativejslexer.cpp b/src/declarative/qml/parser/qdeclarativejslexer.cpp index af955a4..4dfef45 100644 --- a/src/declarative/qml/parser/qdeclarativejslexer.cpp +++ b/src/declarative/qml/parser/qdeclarativejslexer.cpp @@ -619,7 +619,7 @@ int Lexer::lex() } else { setDone(Bad); err = IllegalEscapeSequence; - errmsg = QCoreApplication::translate("QDeclarativeParser", "Illegal escape squence"); + errmsg = QCoreApplication::translate("QDeclarativeParser", "Illegal escape sequence"); } } else if (current == 'x') state = InHexEscape; diff --git a/src/gui/embedded/qkbd_defaultmap_qws_p.h b/src/gui/embedded/qkbd_defaultmap_qws_p.h index 0444093..017f56d 100644 --- a/src/gui/embedded/qkbd_defaultmap_qws_p.h +++ b/src/gui/embedded/qkbd_defaultmap_qws_p.h @@ -649,6 +649,7 @@ const QWSKeyboard::Mapping QWSKbPrivate::s_keymap_default[] = { { 119, 0xffff, 0x01000008, 0x00, 0x00, 0x0000 }, { 138, 0xffff, 0x01000058, 0x00, 0x00, 0x0000 }, { 139, 0xffff, 0x01000055, 0x00, 0x00, 0x0000 }, + { 152, 0xffff, 0x010000ba, 0x00, 0x00, 0x0000 }, }; diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp index 859f7fc..b8586ce 100644 --- a/src/gui/graphicsview/qgridlayoutengine.cpp +++ b/src/gui/graphicsview/qgridlayoutengine.cpp @@ -194,7 +194,8 @@ void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo) for (int k = 0; k < span; ++k) { boxes[start + k].combine(extras[k]); - stretches[start + k] = qMax(stretches[start + k], stretch); + if (stretch != 0) + stretches[start + k] = qMax(stretches[start + k], stretch); } } multiCellMap.clear(); @@ -1481,7 +1482,7 @@ void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, const QLayoutSt QGridLayoutBox *box; if (effectiveRowSpan == 1) { box = &rowBox; - if (!userRowStretch) + if (!userRowStretch && itemStretch != 0) rowStretch = qMax(rowStretch, itemStretch); } else { QGridLayoutMultiCellData &multiCell = diff --git a/src/gui/gui.pro b/src/gui/gui.pro index fe1a595..076fe0a 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -65,15 +65,14 @@ symbian { neon:*-g++* { DEFINES += QT_HAVE_NEON HEADERS += $$NEON_HEADERS - SOURCES += $$NEON_SOURCES DRAWHELPER_NEON_ASM_FILES = $$NEON_ASM - neon_compiler.commands = $$QMAKE_CXX -c -mfpu=neon - neon_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} + neon_compiler.commands = $$QMAKE_CXX -c + neon_compiler.commands += $(CXXFLAGS) -mfpu=neon $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} neon_compiler.dependency_type = TYPE_C neon_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - neon_compiler.input = DRAWHELPER_NEON_ASM_FILES + neon_compiler.input = DRAWHELPER_NEON_ASM_FILES NEON_SOURCES neon_compiler.variable_out = OBJECTS neon_compiler.name = compiling[neon] ${QMAKE_FILE_IN} silent:neon_compiler.commands = @echo compiling[neon] ${QMAKE_FILE_IN} && $$neon_compiler.commands diff --git a/src/gui/image/qgifhandler.cpp b/src/gui/image/qgifhandler.cpp index 4dd4743..7cb7373 100644 --- a/src/gui/image/qgifhandler.cpp +++ b/src/gui/image/qgifhandler.cpp @@ -1046,7 +1046,7 @@ QGifHandler::QGifHandler() { gifFormat = new QGIFFormat; nextDelay = 100; - loopCnt = 1; + loopCnt = -1; frameNumber = -1; scanIsCached = false; } @@ -1192,7 +1192,13 @@ int QGifHandler::loopCount() const QGIFFormat::scan(device(), &imageSizes, &loopCnt); scanIsCached = true; } - return loopCnt-1; // In GIF, loop count is iteration count, so subtract one + + if (loopCnt == 0) + return -1; + else if (loopCnt == -1) + return 0; + else + return loopCnt; } int QGifHandler::currentImageNumber() const diff --git a/src/gui/image/qnativeimage.cpp b/src/gui/image/qnativeimage.cpp index 01baa36..8face87 100644 --- a/src/gui/image/qnativeimage.cpp +++ b/src/gui/image/qnativeimage.cpp @@ -181,15 +181,17 @@ QNativeImage::QNativeImage(int width, int height, QImage::Format format,bool /* if (ok) { xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0); xshminfo.shmaddr = xshmimg->data; - if (shmctl(xshminfo.shmid, IPC_RMID, 0) == -1) - qWarning() << "Error while marking the shared memory segment to be destroyed"; ok = (xshminfo.shmaddr != (char*)-1); if (ok) image = QImage((uchar *)xshmimg->data, width, height, format); } xshminfo.readOnly = false; - if (ok) + if (ok) { ok = XShmAttach(X11->display, &xshminfo); + XSync(X11->display, False); + if (shmctl(xshminfo.shmid, IPC_RMID, 0) == -1) + qWarning() << "Error while marking the shared memory segment to be destroyed"; + } if (!ok) { qWarning() << "QNativeImage: Unable to attach to shared memory segment."; if (xshmimg->data) { diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index ff199b1..1bef64d 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -706,6 +706,7 @@ void QCoeFepInputContext::CancelFepInlineEdit() QInputMethodEvent event(QLatin1String(""), attributes); event.setCommitString(QLatin1String(""), 0, 0); m_preeditString.clear(); + m_inlinePosition = 0; sendEvent(event); } @@ -853,6 +854,7 @@ void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction) QInputMethodEvent event(QLatin1String(""), attributes); event.setCommitString(m_preeditString, 0, 0); m_preeditString.clear(); + m_inlinePosition = 0; sendEvent(event); m_hasTempPreeditString = false; diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 4261e93..0ff3d88 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -222,7 +222,8 @@ qpa { kernel/qplatformglcontext_qpa.h \ kernel/qdesktopwidget_qpa_p.h \ kernel/qplatformeventloopintegration_qpa.h \ - kernel/qplatformcursor_qpa.h + kernel/qplatformcursor_qpa.h \ + kernel/qplatformclipboard_qpa.h SOURCES += \ kernel/qapplication_qpa.cpp \ @@ -244,7 +245,8 @@ qpa { kernel/qplatformwindowformat_qpa.cpp \ kernel/qplatformeventloopintegration_qpa.cpp \ kernel/qplatformglcontext_qpa.cpp \ - kernel/qplatformcursor_qpa.cpp + kernel/qplatformcursor_qpa.cpp \ + kernel/qplatformclipboard_qpa.cpp contains(QT_CONFIG, glib) { SOURCES += \ diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 5c4a314..e91fe04 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -2771,7 +2771,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) { for (int i = 0; i < leaveList.size(); ++i) { w = leaveList.at(i); if (!QApplication::activeModalWidget() || QApplicationPrivate::tryModalHelper(w, 0)) { -#if defined(Q_WS_WIN) || defined(Q_WS_X11) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_MAC) if (leaveAfterRelease == w) leaveAfterRelease = 0; #endif @@ -3142,13 +3142,11 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, // Dispatch enter/leave if: // 1) the mouse grabber is an alien widget // 2) the button is released on an alien widget - QWidget *enter = 0; if (nativeGuard) enter = alienGuard ? alienWidget : nativeWidget; else // The receiver is typically deleted on mouse release with drag'n'drop. enter = QApplication::widgetAt(event->globalPos()); - dispatchEnterLeave(enter, leaveAfterRelease); leaveAfterRelease = 0; lastMouseReceiver = enter; @@ -3695,15 +3693,6 @@ void QApplication::changeOverrideCursor(const QCursor &cursor) if (qApp->d_func()->cursor_list.isEmpty()) return; qApp->d_func()->cursor_list.removeFirst(); -#ifdef QT_MAC_USE_COCOA - // We use native NSCursor stacks in Cocoa. The currentCursor is the - // top of this stack. So to avoid flickering of cursor, we have to - // change the cusor instead of pop-ing the existing OverrideCursor - // and pushing the new one. - qApp->d_func()->cursor_list.prepend(cursor); - qt_cocoaChangeOverrideCursor(cursor); - return; -#endif setOverrideCursor(cursor); } #endif @@ -4434,6 +4423,24 @@ bool QApplication::notify(QObject *receiver, QEvent *e) break; } #endif // QT_NO_GESTURES +#ifdef QT_MAC_USE_COCOA + case QEvent::Enter: + if (receiver->isWidgetType()) { + QWidget *w = static_cast<QWidget *>(receiver); + if (w->testAttribute(Qt::WA_AcceptTouchEvents)) + qt_widget_private(w)->registerTouchWindow(true); + } + res = d->notify_helper(receiver, e); + break; + case QEvent::Leave: + if (receiver->isWidgetType()) { + QWidget *w = static_cast<QWidget *>(receiver); + if (w->testAttribute(Qt::WA_AcceptTouchEvents)) + qt_widget_private(w)->registerTouchWindow(false); + } + res = d->notify_helper(receiver, e); + break; +#endif default: res = d->notify_helper(receiver, e); break; diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index b22a6b3..f607a72 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -165,6 +165,7 @@ QT_BEGIN_NAMESPACE //for qt_mac.h QPaintDevice *qt_mac_safe_pdev = 0; QList<QMacWindowChangeEvent*> *QMacWindowChangeEvent::change_events = 0; +QPointer<QWidget> topLevelAt_cache = 0; /***************************************************************************** Internal variables and functions @@ -192,7 +193,6 @@ static bool qt_mac_previous_press_in_popup_mode = false; static bool qt_mac_no_click_through_mode = false; static int tablet_button_state = 0; #endif -QPointer<QWidget> qt_mouseover; #if defined(QT_DEBUG) static bool appNoGrab = false; // mouse/keyboard grabbing #endif @@ -216,11 +216,12 @@ extern bool qt_mac_can_clickThrough(const QWidget *); //qwidget_mac.cpp extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp extern OSWindowRef qt_mac_window_for(const QWidget*); //qwidget_mac.cpp extern QWidget *qt_mac_find_window(OSWindowRef); //qwidget_mac.cpp -extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.cpp +extern void qt_mac_set_cursor(const QCursor *); //qcursor_mac.cpp extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp extern void qt_mac_command_set_enabled(MenuRef, UInt32, bool); //qmenu_mac.cpp extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); // qapplication.cpp +extern void qt_mac_update_cursor(); // qcursor_mac.mm // Forward Decls void onApplicationWindowChangedActivation( QWidget*widget, bool activated ); @@ -1364,43 +1365,16 @@ void QApplication::setMainWidget(QWidget *mainWidget) /***************************************************************************** QApplication cursor stack *****************************************************************************/ -#ifdef QT_MAC_USE_COCOA -void QApplicationPrivate::disableUsageOfCursorRects(bool disable) -{ - // In Cocoa there are two competing ways of setting the cursor; either - // by using cursor rects (see qcocoaview_mac.mm), or by pushing/popping - // the cursor manually. When we use override cursors, it makes most sense - // to use the latter. But then we need to tell cocoa to stop using the - // first approach so it doesn't change the cursor back when hovering over - // a cursor rect: - QWidgetList topLevels = qApp->topLevelWidgets(); - for (int i=0; i<topLevels.size(); ++i) { - if (NSWindow *window = qt_mac_window_for(topLevels.at(i))) - disable ? [window disableCursorRects] : [window enableCursorRects]; - } -} - -void QApplicationPrivate::updateOverrideCursor() -{ - // Sometimes Cocoa forgets that we have set a Cursor - // manually. In those cases, remind it again: - if (QCursor *override = qApp->overrideCursor()) - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*override)) set]; -} -#endif void QApplication::setOverrideCursor(const QCursor &cursor) { qApp->d_func()->cursor_list.prepend(cursor); #ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - if (qApp->d_func()->cursor_list.size() == 1) - qApp->d_func()->disableUsageOfCursorRects(true); - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) push]; + qt_mac_update_cursor(); #else if (qApp && qApp->activeWindow()) - qt_mac_set_cursor(&qApp->d_func()->cursor_list.first(), QCursor::pos()); + qt_mac_set_cursor(&qApp->d_func()->cursor_list.first()); #endif } @@ -1411,14 +1385,11 @@ void QApplication::restoreOverrideCursor() qApp->d_func()->cursor_list.removeFirst(); #ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - [NSCursor pop]; - if (qApp->d_func()->cursor_list.isEmpty()) - qApp->d_func()->disableUsageOfCursorRects(false); + qt_mac_update_cursor(); #else if (qApp && qApp->activeWindow()) { const QCursor def(Qt::ArrowCursor); - qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first(), QCursor::pos()); + qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first()); } #endif } @@ -1431,30 +1402,54 @@ QWidget *QApplication::topLevelAt(const QPoint &p) qt_mac_window_at(p.x(), p.y(), &widget); return widget; #else + // Use a cache to avoid iterate through the whole list of windows for all + // calls to to topLevelAt. We e.g. do this for each and every mouse + // move since we need to find the widget under mouse: + if (topLevelAt_cache && topLevelAt_cache->frameGeometry().contains(p)) + return topLevelAt_cache; + + // INVARIANT: Cache miss. Go through the list if windows instead: + QMacCocoaAutoReleasePool pool; + NSPoint cocoaPoint = flipPoint(p); NSInteger windowCount; NSCountWindows(&windowCount); if (windowCount <= 0) return 0; // There's no window to find! - QMacCocoaAutoReleasePool pool; - NSPoint cocoaPoint = flipPoint(p); + QVarLengthArray<NSInteger> windowList(windowCount); NSWindowList(windowCount, windowList.data()); + int firstQtWindowFound = -1; for (int i = 0; i < windowCount; ++i) { NSWindow *window = [NSApp windowWithWindowNumber:windowList[i]]; - if (window && NSPointInRect(cocoaPoint, [window frame])) { + if (window) { QWidget *candidateWindow = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; - // Check to see if there's a hole in the window where the mask is. - // If there is, we should just continue to see if there is a window below. - if (candidateWindow && !candidateWindow->mask().isEmpty()) { - QPoint localPoint = candidateWindow->mapFromGlobal(p); - if (!candidateWindow->mask().contains(localPoint)) { - continue; + if (candidateWindow && firstQtWindowFound == -1) + firstQtWindowFound = i; + + if (NSPointInRect(cocoaPoint, [window frame])) { + // Check to see if there's a hole in the window where the mask is. + // If there is, we should just continue to see if there is a window below. + if (candidateWindow && !candidateWindow->mask().isEmpty()) { + QPoint localPoint = candidateWindow->mapFromGlobal(p); + if (!candidateWindow->mask().contains(localPoint)) + continue; + else + return candidateWindow; + } else { + if (i == firstQtWindowFound) { + // The cache will only work when the window under mouse is + // top most (that is, not partially obscured by other windows. + // And we only set it if no mask is present to optimize for the common case: + topLevelAt_cache = candidateWindow; + } + return candidateWindow; } } - return candidateWindow; } } - return 0; // Couldn't find a window at this point + + topLevelAt_cache = 0; + return 0; #endif } @@ -1480,8 +1475,8 @@ void QApplicationPrivate::enterModal_sys(QWidget *widget) if (!qt_modal_stack) qt_modal_stack = new QWidgetList; - dispatchEnterLeave(0, qt_mouseover); - qt_mouseover = 0; + dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; qt_modal_stack->insert(0, widget); if (!app_do_modal) @@ -1512,8 +1507,8 @@ void QApplicationPrivate::leaveModal_sys(QWidget *widget) w = grabber; else w = QApplication::widgetAt(p.x(), p.y()); - dispatchEnterLeave(w, qt_mouseover); // send synthetic enter event - qt_mouseover = w; + dispatchEnterLeave(w, qt_last_mouse_receiver); // send synthetic enter event + qt_last_mouse_receiver = w; } #ifdef QT_MAC_USE_COCOA if (!qt_mac_is_macsheet(widget)) @@ -1938,7 +1933,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event } } } - qt_mac_set_cursor(&cursor, QPoint(where.h, where.v)); + qt_mac_set_cursor(&cursor); } //This mouse button state stuff looks like this on purpose @@ -2132,20 +2127,20 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event QWidget * const enterLeaveWidget = (inPopupMode || ekind == kEventMouseUp) ? QApplication::widgetAt(where.h, where.v) : static_cast<QWidget*>(widget); - if ((QWidget *) qt_mouseover != enterLeaveWidget || inNonClientArea) { + if ((QWidget *) qt_last_mouse_receiver != enterLeaveWidget || inNonClientArea) { #ifdef DEBUG_MOUSE_MAPS qDebug("Entering: %p - %s (%s), Leaving %s (%s)", (QWidget*)enterLeaveWidget, enterLeaveWidget ? enterLeaveWidget->metaObject()->className() : "none", enterLeaveWidget ? enterLeaveWidget->objectName().toLocal8Bit().constData() : "", - qt_mouseover ? qt_mouseover->metaObject()->className() : "none", - qt_mouseover ? qt_mouseover->objectName().toLocal8Bit().constData() : ""); + qt_last_mouse_receiver ? qt_last_mouse_receiver->metaObject()->className() : "none", + qt_last_mouse_receiver ? qt_last_mouse_receiver->objectName().toLocal8Bit().constData() : ""); #endif QWidget * const mouseGrabber = QWidget::mouseGrabber(); if (inPopupMode) { QWidget *enter = enterLeaveWidget; - QWidget *leave = qt_mouseover; + QWidget *leave = qt_last_mouse_receiver; if (mouseGrabber) { QWidget * const popupWidget = qApp->activePopupWidget(); if (leave == popupWidget) @@ -2155,15 +2150,15 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event if ((enter == mouseGrabber && leave == popupWidget) || (leave == mouseGrabber && enter == popupWidget)) { QApplicationPrivate::dispatchEnterLeave(enter, leave); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; } } else { QApplicationPrivate::dispatchEnterLeave(enter, leave); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; } - } else if ((!qt_button_down || !qt_mouseover) && !mouseGrabber && !leaveAfterRelease) { - QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_mouseover); - qt_mouseover = enterLeaveWidget; + } else if ((!qt_button_down || !qt_last_mouse_receiver) && !mouseGrabber && !leaveAfterRelease) { + QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_last_mouse_receiver); + qt_last_mouse_receiver = enterLeaveWidget; } } break; } @@ -2240,7 +2235,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event if (leaveAfterRelease) { QWidget *enter = QApplication::widgetAt(where.h, where.v); QApplicationPrivate::dispatchEnterLeave(enter, leaveAfterRelease); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; leaveAfterRelease = 0; } @@ -2503,7 +2498,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event void QApplicationPrivate::qt_initAfterNSAppStarted() { setupAppleEvents(); - updateOverrideCursor(); + qt_mac_update_cursor(); } void QApplicationPrivate::setupAppleEvents() @@ -3080,7 +3075,7 @@ void onApplicationWindowChangedActivation(QWidget *widget, bool activated) } QMenuBar::macUpdateMenuBar(); - QApplicationPrivate::updateOverrideCursor(); + qt_mac_update_cursor(); #else Q_UNUSED(widget); Q_UNUSED(activated); @@ -3115,6 +3110,7 @@ void onApplicationChangedActivation( bool activated ) app->setActiveWindow(tmp_w); } QMenuBar::macUpdateMenuBar(); + qt_mac_update_cursor(); } else { // de-activated QApplicationPrivate *priv = [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] qAppPrivate]; while (priv->inPopupMode()) diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index f9508a7..a6815a8 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -489,8 +489,6 @@ public: #ifdef QT_MAC_USE_COCOA static void qt_initAfterNSAppStarted(); static void setupAppleEvents(); - static void updateOverrideCursor(); - static void disableUsageOfCursorRects(bool disable); #endif static bool qt_mac_apply_settings(); #endif @@ -508,6 +506,8 @@ public: static void processEnterEvent(QWindowSystemInterfacePrivate::EnterEvent *e); static void processLeaveEvent(QWindowSystemInterfacePrivate::LeaveEvent *e); + static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e); + static void processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e); // static void reportScreenCount(int count); diff --git a/src/gui/kernel/qapplication_qpa.cpp b/src/gui/kernel/qapplication_qpa.cpp index ece035c..cd76adf 100644 --- a/src/gui/kernel/qapplication_qpa.cpp +++ b/src/gui/kernel/qapplication_qpa.cpp @@ -79,7 +79,6 @@ int qt_last_x = 0; int qt_last_y = 0; QPointer<QWidget> qt_last_mouse_receiver = 0; -static Qt::KeyboardModifiers modifiers = Qt::NoModifier; static Qt::MouseButtons buttons = Qt::NoButton; static ulong mousePressTime; static Qt::MouseButton mousePressButton = Qt::NoButton; @@ -111,6 +110,9 @@ void QApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate case QWindowSystemInterfacePrivate::Leave: QApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e)); break; + case QWindowSystemInterfacePrivate::ActivatedWindow: + QApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e)); + break; case QWindowSystemInterfacePrivate::Close: QApplicationPrivate::processCloseEvent( static_cast<QWindowSystemInterfacePrivate::CloseEvent *>(e)); @@ -441,11 +443,11 @@ void QApplication::alert(QWidget *, int) { } -static void init_platform(const QString &name) +static void init_platform(const QString &name, const QString &platformPluginPath) { - QApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name); + QApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, platformPluginPath); if (!QApplicationPrivate::platform_integration) { - QStringList keys = QPlatformIntegrationFactory::keys(); + QStringList keys = QPlatformIntegrationFactory::keys(platformPluginPath); QString fatalMessage = QString::fromLatin1("Failed to load platform plugin \"%1\". Available platforms are: \n").arg(name); foreach(QString key, keys) { @@ -510,6 +512,7 @@ void qt_init(QApplicationPrivate *priv, int type) } QList<QByteArray> pluginList; + QString platformPluginPath = QLatin1String(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH")); QString platformName = QLatin1String(qgetenv("QT_QPA_PLATFORM")); // Get command line params @@ -524,6 +527,9 @@ void qt_init(QApplicationPrivate *priv, int type) if (arg == "-fn" || arg == "-font") { if (++i < argc) appFont = QString::fromLocal8Bit(argv[i]); + } else if (arg == "-platformpluginpath") { + if (++i < argc) + platformPluginPath = QLatin1String(argv[i]); } else if (arg == "-platform") { if (++i < argc) platformName = QLatin1String(argv[i]); @@ -547,7 +553,7 @@ void qt_init(QApplicationPrivate *priv, int type) } #endif - init_platform(platformName); + init_platform(platformName, platformPluginPath); init_plugins(pluginList); QColormap::initialize(); @@ -724,7 +730,7 @@ void QApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mouse // qDebug() << "sending mouse ev." << ev.type() << localPoint << globalPoint << ev.button() << ev.buttons() << mouseWidget << "mouse grabber" << implicit_mouse_grabber; - QMouseEvent ev(type, localPoint, globalPoint, button, buttons, modifiers); + QMouseEvent ev(type, localPoint, globalPoint, button, buttons, QApplication::keyboardModifiers()); QList<QWeakPointer<QPlatformCursor> > cursors = QPlatformCursorPrivate::getInstances(); foreach (QWeakPointer<QPlatformCursor> cursor, cursors) { @@ -732,7 +738,15 @@ void QApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mouse cursor.data()->pointerEvent(ev); } + int oldOpenPopupCount = openPopupCount; QApplication::sendSpontaneousEvent(mouseWidget, &ev); + +#ifndef QT_NO_CONTEXTMENU + if (type == QEvent::MouseButtonPress && button == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) { + QContextMenuEvent e(QContextMenuEvent::Mouse, localPoint, globalPoint, QApplication::keyboardModifiers()); + QApplication::sendSpontaneousEvent(mouseWidget, &e); + } +#endif // QT_NO_CONTEXTMENU } @@ -771,7 +785,7 @@ void QApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wheel p = mouseWidget->mapFromGlobal(globalPoint); } - QWheelEvent ev(p, globalPoint, e->delta, buttons, modifiers, + QWheelEvent ev(p, globalPoint, e->delta, buttons, QApplication::keyboardModifiers(), e->orient); QApplication::sendSpontaneousEvent(mouseWidget, &ev); } @@ -802,7 +816,6 @@ void QApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyEven if (app_do_modal && !qt_try_modal(focusW, e->keyType)) return; - modifiers = e->modifiers; QKeyEvent ev(e->keyType, e->key, e->modifiers, e->unicode, e->repeat, e->repeatCount); QApplication::sendSpontaneousEvent(focusW, &ev); } @@ -823,6 +836,10 @@ void QApplicationPrivate::processLeaveEvent(QWindowSystemInterfacePrivate::Leave } +void QApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e) +{ + QApplication::setActiveWindow(e->activated.data()); +} void QApplicationPrivate::processGeometryChangeEvent(QWindowSystemInterfacePrivate::GeometryChangeEvent *e) { diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index 3a70dd5..a9f57f4 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -1616,7 +1616,9 @@ void qt_init(QApplicationPrivate * /* priv */, int) qRegisterMetaType<WId>("WId"); } -extern void qt_cleanup_symbianFontDatabaseExtras(); // qfontdatabase_s60.cpp +#ifdef QT_NO_FREETYPE +extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp +#endif /***************************************************************************** qt_cleanup() - cleans up when the application is finished @@ -1633,7 +1635,9 @@ void qt_cleanup() QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles QPixmapCache::clear(); // Has to happen now, since QS60PixmapData has FBS handles - qt_cleanup_symbianFontDatabaseExtras(); +#ifdef QT_NO_FREETYPE + qt_cleanup_symbianFontDatabase(); +#endif // S60 structure and window server session are freed in eventdispatcher destructor as they are needed there // It's important that this happens here, before the event dispatcher gets @@ -2019,6 +2023,9 @@ int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); } #endif +#ifdef QT_SOFTKEYS_ENABLED + QSoftKeyManager::updateSoftKeys(); +#endif break; case EEventFocusLost: if (callSymbianEventFilters(symbianEvent)) diff --git a/src/gui/kernel/qclipboard_qpa.cpp b/src/gui/kernel/qclipboard_qpa.cpp index 92b9e83..b8ce60e 100644 --- a/src/gui/kernel/qclipboard_qpa.cpp +++ b/src/gui/kernel/qclipboard_qpa.cpp @@ -44,73 +44,16 @@ #ifndef QT_NO_CLIPBOARD #include "qmimedata.h" -#include "qapplication.h" +#include "private/qapplication_p.h" +#include "qplatformclipboard_qpa.h" QT_BEGIN_NAMESPACE QT_USE_NAMESPACE - -class QClipboardData -{ -public: - QClipboardData(); - ~QClipboardData(); - - void setSource(QMimeData* s) - { - if (s == src) - return; - delete src; - src = s; - } - QMimeData* source() - { return src; } - - void clear(); - -private: - QMimeData* src; -}; - -QClipboardData::QClipboardData() -{ - src = 0; -} - -QClipboardData::~QClipboardData() -{ - delete src; -} - -void QClipboardData::clear() -{ - delete src; - src = 0; -} - - -static QClipboardData *internalCbData = 0; - -static void cleanupClipboardData() -{ - delete internalCbData; - internalCbData = 0; -} - -static QClipboardData *clipboardData() -{ - if (internalCbData == 0) { - internalCbData = new QClipboardData; - qAddPostRoutine(cleanupClipboardData); - } - return internalCbData; -} - - void QClipboard::clear(Mode mode) { - setText(QString(), mode); + setMimeData(0,mode); } @@ -121,26 +64,25 @@ bool QClipboard::event(QEvent *e) const QMimeData* QClipboard::mimeData(Mode mode) const { - if (mode != Clipboard) return 0; - - QClipboardData *d = clipboardData(); - return d->source(); + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + if (!clipboard->supportsMode(mode)) return 0; + return clipboard->mimeData(mode); } void QClipboard::setMimeData(QMimeData* src, Mode mode) { - if (mode != Clipboard) return; - - QClipboardData *d = clipboardData(); + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + if (!clipboard->supportsMode(mode)) return; - d->setSource(src); + clipboard->setMimeData(src,mode); - emitChanged(QClipboard::Clipboard); + emitChanged(mode); } bool QClipboard::supportsMode(Mode mode) const { - return (mode == Clipboard); + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + return clipboard->supportsMode(mode); } bool QClipboard::ownsMode(Mode mode) const diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm index bf4d9e5..6d7bc19 100644 --- a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm +++ b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm @@ -90,6 +90,9 @@ QT_BEGIN_NAMESPACE extern void onApplicationChangedActivation(bool); // qapplication_mac.mm extern void qt_release_apple_event_handler(); //qapplication_mac.mm +extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp +extern QPointer<QWidget> qt_button_down; // qapplication_mac.cpp + QT_END_NAMESPACE QT_FORWARD_DECLARE_CLASS(QDesktopWidgetImplementation) @@ -254,7 +257,18 @@ static void cleanupCocoaApplicationDelegate() if (reflectionDelegate && [reflectionDelegate respondsToSelector:@selector(applicationDidBecomeActive:)]) [reflectionDelegate applicationDidBecomeActive:notification]; + onApplicationChangedActivation(true); + + if (!QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, 0); + qt_last_mouse_receiver = widgetUnderMouse; + } } - (void)applicationDidResignActive:(NSNotification *)notification @@ -262,7 +276,13 @@ static void cleanupCocoaApplicationDelegate() if (reflectionDelegate && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)]) [reflectionDelegate applicationDidResignActive:notification]; + onApplicationChangedActivation(false); + + if (!QWidget::mouseGrabber()) + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; + qt_button_down = 0; } - (void)applicationDidChangeScreenParameters:(NSNotification *)notification diff --git a/src/gui/kernel/qcocoapanel_mac.mm b/src/gui/kernel/qcocoapanel_mac.mm index a0f17bc..67a12e2 100644 --- a/src/gui/kernel/qcocoapanel_mac.mm +++ b/src/gui/kernel/qcocoapanel_mac.mm @@ -47,9 +47,10 @@ #import <private/qcocoaview_mac_p.h> #import <private/qcocoawindowcustomthemeframe_mac_p.h> #import <private/qcocoaapplication_mac_p.h> -#include <private/qapplication_p.h> -#include <private/qbackingstore_p.h> - +#import <private/qmultitouch_mac_p.h> +#import <private/qapplication_p.h> +#import <private/qbackingstore_p.h> +#import <private/qdnd_p.h> #include <QtGui/QWidget> diff --git a/src/gui/kernel/qcocoapanel_mac_p.h b/src/gui/kernel/qcocoapanel_mac_p.h index e4dd73b..b41a4b5 100644 --- a/src/gui/kernel/qcocoapanel_mac_p.h +++ b/src/gui/kernel/qcocoapanel_mac_p.h @@ -50,20 +50,27 @@ // We mean it. // +#ifndef QCOCOAPANEL_MAC_P +#define QCOCOAPANEL_MAC_P + #include "qmacdefines_mac.h" #ifdef QT_MAC_USE_COCOA #import <Cocoa/Cocoa.h> QT_FORWARD_DECLARE_CLASS(QStringList); +QT_FORWARD_DECLARE_CLASS(QCocoaDropData); @interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel { - bool leftButtonIsRightButton; QStringList *currentCustomDragTypes; + QCocoaDropData *dropData; + NSInteger dragEnterSequence; } + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; - (void)registerDragTypes; +- (void)drawRectOriginal:(NSRect)rect; @end #endif +#endif diff --git a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h index df0f24e..1c9258a 100644 --- a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h +++ b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h @@ -62,7 +62,6 @@ extern void qt_event_request_window_change(QWidget *); // qapplication_mac.mm extern void qt_mac_send_posted_gl_updates(QWidget *widget); // qapplication_mac.mm Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget); - QT_END_NAMESPACE - (id)initWithContentRect:(NSRect)contentRect @@ -145,66 +144,27 @@ QT_END_NAMESPACE - (void)sendEvent:(NSEvent *)event { - QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; - // Cocoa can hold onto the window after we've disavowed its knowledge. So, - // if we get sent an event afterwards just have it go through the super's - // version and don't do any stuff with Qt. - if (!widget) { - [super sendEvent:event]; - return; - } - [self retain]; - QT_MANGLE_NAMESPACE(QCocoaView) *view = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_nativeview_for(widget)); - Qt::MouseButton mouseButton = cocoaButton2QtButton([event buttonNumber]); bool handled = false; - // sometimes need to redirect mouse events to the popup. - QWidget *popup = qAppInstance()->activePopupWidget(); - if (popup && popup != widget) { - switch([event type]) - { - case NSLeftMouseDown: - if (!qt_button_down) - qt_button_down = widget; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton); - // Don't call super here. This prevents us from getting the mouseUp event, - // which we need to send even if the mouseDown event was not accepted. - // (this is standard Qt behavior.) - break; - case NSRightMouseDown: - case NSOtherMouseDown: - if (!qt_button_down) - qt_button_down = widget; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton); - break; - case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonRelease, mouseButton); - qt_button_down = 0; - break; - case NSMouseMoved: - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, Qt::NoButton); - break; - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = view; - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = event; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, mouseButton); - break; - default: - [super sendEvent:event]; - break; - } - } else { - [super sendEvent:event]; + switch([event type]) { + case NSMouseMoved: + // Cocoa sends move events to a parent and all its children under the mouse, much + // like Qt handles hover events. But we only want to handle the move event once, so + // to optimize a bit (since we subscribe for move event for all views), we handle it + // here before this logic happends. Note: it might be tempting to do this shortcut for + // all mouse events. The problem is that Cocoa does more than just find the correct view + // when sending the event, like raising windows etc. So avoid it as much as possible: + handled = qt_mac_handleMouseEvent(event, QEvent::MouseMove, Qt::NoButton, 0); + break; + default: + break; } - if (!handled) - qt_mac_dispatchNCMouseMessage(self, event, [self QT_MANGLE_NAMESPACE(qt_qwidget)], leftButtonIsRightButton); - + if (!handled) { + [super sendEvent:event]; + qt_mac_handleNonClientAreaMouseEvent(self, event); + } [self release]; } @@ -237,6 +197,56 @@ QT_END_NAMESPACE return [super frameViewClassForStyleMask:styleMask]; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +- (void)touchesBeganWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesMovedWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesEndedWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesCancelledWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + -(void)registerDragTypes { // Calling registerForDraggedTypes below is slow, so only do @@ -259,21 +269,47 @@ QT_END_NAMESPACE NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil]; // Add custom types supported by the application. for (int i = 0; i < customTypes.size(); i++) { - [supportedTypes addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))]; + [supportedTypes addObject:qt_mac_QStringToNSString(customTypes[i])]; } [self registerForDraggedTypes:supportedTypes]; } } -- (QWidget *)dragTargetHitTest:(id <NSDraggingInfo>)sender +- (void)removeDropData +{ + if (dropData) { + delete dropData; + dropData = 0; + } +} + +- (void)addDropData:(id <NSDraggingInfo>)sender { - // Do a hittest to find the NSView under the - // mouse, and return the corresponding QWidget: - NSPoint windowPoint = [sender draggingLocation]; - NSView *candidateView = [[self contentView] hitTest:windowPoint]; - if (![candidateView isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) - return 0; - return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(candidateView) qt_qwidget]; + [self removeDropData]; + CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name]; + dropData = new QCocoaDropData(dropPasteboard); +} + +- (void)changeDraggingCursor:(NSDragOperation)newOperation +{ + static SEL action = nil; + static bool operationSupported = false; + if (action == nil) { + action = NSSelectorFromString(@"operationNotAllowedCursor"); + if ([NSCursor respondsToSelector:action]) { + operationSupported = true; + } + } + if (operationSupported) { + NSCursor *notAllowedCursor = [NSCursor performSelector:action]; + bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor); + if (newOperation == NSDragOperationNone && !isNotAllowedCursor) { + [notAllowedCursor push]; + } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) { + [notAllowedCursor pop]; + } + + } } - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender @@ -286,64 +322,211 @@ QT_END_NAMESPACE // registerForDraggedTypes on the views will severly degrade initialization time // for an application that uses a lot of drag subscribing widgets. - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = QApplication::widgetAt(globalPoint); + *currentDragTarget() = qwidget; + if (!qwidget) return [super draggingEntered:sender]; - if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) + if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) return NSDragOperationNone; - *currentDragTarget() = target; - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender]; + [self addDropData:sender]; + + QMimeData *mimeData = dropData; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); + QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + if ([sender draggingSource] != nil) { + // modifier flags might have changed, update it here since we don't send any input events. + QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); + modifiers = QApplication::keyboardModifiers(); + } else { + // when the source is from another application the above technique will not work. + modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); + } + + // send the drag enter event to the widget. + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + QDragEnterEvent qDEEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + QApplication::sendEvent(qwidget, &qDEEvent); + + if (!qDEEvent.isAccepted()) { + // The enter event was not accepted. We mark this by removing + // the drop data so we don't send subsequent drag move events: + [self removeDropData]; + [self changeDraggingCursor:NSDragOperationNone]; + return NSDragOperationNone; + } else { + // Send a drag move event immediately after a drag enter event (as per documentation). + QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + qDMEvent.setDropAction(qDEEvent.dropAction()); + qDMEvent.accept(); // accept by default, since enter event was accepted. + QApplication::sendEvent(qwidget, &qDMEvent); + + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { + // Since we accepted the drag enter event, the widget expects + // future drage move events. + nsActions = NSDragOperationNone; + // Save as ignored in the answer rect. + qDMEvent.setDropAction(Qt::IgnoreAction); + } else { + nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction()); + } + + QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent); + [self changeDraggingCursor:nsActions]; + return nsActions; + } } -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) - return [super draggingUpdated:sender]; - - if (target == *currentDragTarget()) { - // The drag continues to move over the widget that we have sendt - // a draggingEntered message to. So just update the view: - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingUpdated:sender]; - } else { - // The widget under the mouse has changed. - // So we need to fake enter/leave events: - if (*currentDragTarget()) - [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender]; - if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) { - *currentDragTarget() = 0; - return NSDragOperationNone; + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = QApplication::widgetAt(globalPoint); + if (!qwidget) + return [super draggingEntered:sender]; + + // First, check if the widget under the mouse has changed since the + // last drag move events. If so, we need to change target, and dispatch + // syntetic drag enter/leave events: + if (qwidget != *currentDragTarget()) { + if (*currentDragTarget() && dropData) { + QDragLeaveEvent de; + QApplication::sendEvent(*currentDragTarget(), &de); + [self removeDropData]; } - *currentDragTarget() = target; - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender]; + return [self draggingEntered:sender]; } + + if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) + return NSDragOperationNone; + + // If we have no drop data (which will be assigned inside draggingEntered), it means + // that the current drag target did not accept the enter event. If so, we ignore + // subsequent move events as well: + if (dropData == 0) { + [self changeDraggingCursor:NSDragOperationNone]; + return NSDragOperationNone; + } + + // If the mouse is still within the accepted rect (provided by + // the application on a previous event), we follow the optimization + // and just return the answer given at that point: + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + if (qt_mac_mouse_inside_answer_rect(localPoint) + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) { + NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction)); + [self changeDraggingCursor:operation]; + return operation; + } + + QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; + Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions); + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + // Update modifiers: + if ([sender draggingSource] != nil) { + QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); + modifiers = QApplication::keyboardModifiers(); + } else { + modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); + } + + QMimeData *mimeData = dropData; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + + // Insert the same drop action on the event according to + // what the application told us it should be on the previous event: + QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons() + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers()) + qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction); + + // Now, end the drag move event to the widget: + qDMEvent.accept(); + QApplication::sendEvent(qwidget, &qDMEvent); + + NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction()); + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { + // Ignore this event (we will still receive further + // notifications), save as ignored in the answer rect: + operation = NSDragOperationNone; + qDMEvent.setDropAction(Qt::IgnoreAction); + } + + qt_mac_copy_answer_rect(qDMEvent); + [self changeDraggingCursor:operation]; + + return operation; } -- (void)draggingExited:(id < NSDraggingInfo >)sender +- (void)draggingExited:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = *currentDragTarget(); + if (!qwidget) return [super draggingExited:sender]; - if (*currentDragTarget()) { - [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender]; - *currentDragTarget() = 0; + if (dropData) { + QDragLeaveEvent de; + QApplication::sendEvent(qwidget, &de); + [self removeDropData]; } + + // Clean-up: + [self removeDropData]; + *currentDragTarget() = 0; + [self changeDraggingCursor:NSDragOperationEvery]; } -- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) - return [super performDragOperation:sender]; - - BOOL dropResult = NO; - if (*currentDragTarget()) { - dropResult = [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) performDragOperation:sender]; - *currentDragTarget() = 0; - } - return dropResult; + QWidget *qwidget = *currentDragTarget(); + if (!qwidget) + return NO; + + *currentDragTarget() = 0; + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + [self addDropData:sender]; + + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); + QMimeData *mimeData = dropData; + + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + if (QDragManager::self()->object) + QDragManager::self()->dragPrivate()->target = qwidget; + + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + QDropEvent de(localPoint, qtAllowed, mimeData, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QApplication::sendEvent(qwidget, &de); + + if (QDragManager::self()->object) + QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); + + return de.isAccepted(); } - (void)displayIfNeeded @@ -394,8 +577,8 @@ static bool firstDrawingInvocation = true; - (void)drawRectSpecial:(NSRect)rect { // Call the original drawing method. - [self drawRectOriginal:rect]; - NSWindow *window = [self window]; + [id(self) drawRectOriginal:rect]; + NSWindow *window = [id(self) window]; NSToolbar *toolbar = [window toolbar]; if(!toolbar) { // There is no toolbar, we have to draw a line on top of the line drawn by Cocoa. @@ -415,3 +598,11 @@ static bool firstDrawingInvocation = true; } } } + +- (void)drawRectOriginal:(NSRect)rect +{ + Q_UNUSED(rect) + // This method implementation is here to silenct the compiler. + // See drawRectSpecial for information. +} + diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 5ea893c..f0ae886 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -49,7 +49,6 @@ #include <private/qt_cocoa_helpers_mac_p.h> #include <private/qdnd_p.h> #include <private/qmacinputcontext_p.h> -#include <private/qmultitouch_mac_p.h> #include <private/qevent_p.h> #include <private/qbackingstore_p.h> @@ -76,70 +75,15 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(DnDParams, qMacDnDParams); - -extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm +extern void qt_mac_update_cursor(); // qcursor_mac.mm extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp +extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm -extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm +extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); // qwidget_mac.mm extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); - -struct dndenum_mapper -{ - NSDragOperation mac_code; - Qt::DropAction qt_code; - bool Qt2Mac; -}; - -static dndenum_mapper dnd_enums[] = { - { NSDragOperationLink, Qt::LinkAction, true }, - { NSDragOperationMove, Qt::MoveAction, true }, - { NSDragOperationCopy, Qt::CopyAction, true }, - { NSDragOperationGeneric, Qt::CopyAction, false }, - { NSDragOperationEvery, Qt::ActionMask, false }, - { NSDragOperationNone, Qt::IgnoreAction, false } -}; - -static NSDragOperation qt_mac_mapDropAction(Qt::DropAction action) -{ - for (int i=0; dnd_enums[i].qt_code; i++) { - if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) { - return dnd_enums[i].mac_code; - } - } - return NSDragOperationNone; -} - -static NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions) -{ - NSDragOperation nsActions = NSDragOperationNone; - for (int i=0; dnd_enums[i].qt_code; i++) { - if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code)) - nsActions |= dnd_enums[i].mac_code; - } - return nsActions; -} - -static Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions) -{ - Qt::DropAction action = Qt::IgnoreAction; - for (int i=0; dnd_enums[i].mac_code; i++) { - if (nsActions & dnd_enums[i].mac_code) - return dnd_enums[i].qt_code; - } - return action; -} - -static Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions) -{ - Qt::DropActions actions = Qt::IgnoreAction; - for (int i=0; dnd_enums[i].mac_code; i++) { - if (nsActions & dnd_enums[i].mac_code) - actions |= dnd_enums[i].qt_code; - } - return actions; -} +extern QWidget *mac_mouse_grabber; +extern bool qt_mac_clearDirtyOnWidgetInsideDrawWidget; // qwidget.cpp static QColor colorFrom(NSColor *color) { @@ -185,6 +129,7 @@ extern "C" { extern NSString *NSTextInputReplacementRangeAttributeName; } +//#define ALIEN_DEBUG 1 #ifdef ALIEN_DEBUG static int qCocoaViewCount = 0; #endif @@ -202,12 +147,14 @@ static int qCocoaViewCount = 0; #ifdef ALIEN_DEBUG ++qCocoaViewCount; - qDebug() << "init: qCocoaViewCount is" << qCocoaViewCount; + qDebug() << "Alien: create native view for" << widget << ". qCocoaViewCount is:" << qCocoaViewCount; #endif composing = false; sendKeyEvents = true; fromKeyDownEvent = false; + alienTouchCount = 0; + [self setHidden:YES]; return self; } @@ -222,246 +169,15 @@ static int qCocoaViewCount = 0; object:self]; } -- (void)resetCursorRects -{ - // [NSView addCursorRect] is slow, so bail out early if we can: - if (NSIsEmptyRect([self visibleRect])) - return; - - QWidget *cursorWidget = qwidget; - - if (cursorWidget->testAttribute(Qt::WA_TransparentForMouseEvents)) - cursorWidget = QApplication::widgetAt(qwidget->mapToGlobal(qwidget->rect().center())); - - if (cursorWidget == 0) - return; - - if (!cursorWidget->testAttribute(Qt::WA_SetCursor)) { - [super resetCursorRects]; - return; - } - - QRegion mask = qt_widget_private(cursorWidget)->extra->mask; - NSCursor *nscursor = static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursorWidget->cursor())); - // The mask could have the WA_MouseNoMask attribute set and that means that we have to ignore the mask. - if (mask.isEmpty() || cursorWidget->testAttribute(Qt::WA_MouseNoMask)) { - [self addCursorRect:[qt_mac_nativeview_for(cursorWidget) visibleRect] cursor:nscursor]; - } else { - const QVector<QRect> &rects = mask.rects(); - for (int i = 0; i < rects.size(); ++i) { - const QRect &rect = rects.at(i); - [self addCursorRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()) cursor:nscursor]; - } - } -} - -- (void)removeDropData -{ - if (dropData) { - delete dropData; - dropData = 0; - } -} - -- (void)addDropData:(id <NSDraggingInfo>)sender -{ - [self removeDropData]; - CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name]; - dropData = new QCocoaDropData(dropPasteboard); -} - -- (void)changeDraggingCursor:(NSDragOperation)newOperation -{ - static SEL action = nil; - static bool operationSupported = false; - if (action == nil) { - action = NSSelectorFromString(@"operationNotAllowedCursor"); - if ([NSCursor respondsToSelector:action]) { - operationSupported = true; - } - } - if (operationSupported) { - NSCursor *notAllowedCursor = [NSCursor performSelector:action]; - bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor); - if (newOperation == NSDragOperationNone && !isNotAllowedCursor) { - [notAllowedCursor push]; - } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) { - [notAllowedCursor pop]; - } - - } -} - -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - dragEnterSequence = [sender draggingSequenceNumber]; - [self addDropData:sender]; - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint posDrag(localPoint.x, localPoint.y); - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); - QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - if ([sender draggingSource] != nil) { - // modifier flags might have changed, update it here since we don't send any input events. - QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); - modifiers = QApplication::keyboardModifiers(); - } else { - // when the source is from another application the above technique will not work. - modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); - } - // send the drag enter event to the widget. - QDragEnterEvent qDEEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - QApplication::sendEvent(qwidget, &qDEEvent); - if (!qDEEvent.isAccepted()) { - // widget is not interested in this drag, so ignore this drop data. - [self removeDropData]; - [self changeDraggingCursor:NSDragOperationNone]; - return NSDragOperationNone; - } else { - // save the mouse position, used by draggingExited handler. - DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; - dndParams->activeDragEnterPos = windowPoint; - // send a drag move event immediately after a drag enter event (as per documentation). - QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - qDMEvent.setDropAction(qDEEvent.dropAction()); - qDMEvent.accept(); // accept by default, since enter event was accepted. - QApplication::sendEvent(qwidget, &qDMEvent); - if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { - // since we accepted the drag enter event, the widget expects - // future drage move events. - // ### check if we need to treat this like the drag enter event. - nsActions = NSDragOperationNone; - // Save as ignored in the answer rect. - qDMEvent.setDropAction(Qt::IgnoreAction); - } else { - nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction()); - } - QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent); - [self changeDraggingCursor:nsActions]; - return nsActions; - } - } -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - // in cases like QFocusFrame, the view under the mouse might - // not have received the drag enter. Generate a synthetic - // drag enter event for that view. - if (dragEnterSequence != [sender draggingSequenceNumber]) - [self draggingEntered:sender]; - // drag enter event was rejected, so ignore the move event. - if (dropData == 0) { - [self changeDraggingCursor:NSDragOperationNone]; - return NSDragOperationNone; - } - // return last value, if we are still in the answerRect. - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - QPoint posDrag(localPoint.x, localPoint.y); - if (qt_mac_mouse_inside_answer_rect(posDrag) - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) { - NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction)); - [self changeDraggingCursor:operation]; - return operation; - } - // send drag move event to the widget - QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; - Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions); - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - if ([sender draggingSource] != nil) { - // modifier flags might have changed, update it here since we don't send any input events. - QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); - modifiers = QApplication::keyboardModifiers(); - } else { - // when the source is from another application the above technique will not work. - modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); - } - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons() - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers()) - qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction); - qDMEvent.accept(); - QApplication::sendEvent(qwidget, &qDMEvent); - - NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction()); - if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { - // ignore this event (we will still receive further notifications) - operation = NSDragOperationNone; - // Save as ignored in the answer rect. - qDMEvent.setDropAction(Qt::IgnoreAction); - } - qt_mac_copy_answer_rect(qDMEvent); - [self changeDraggingCursor:operation]; - return operation; -} - -- (void)draggingExited:(id < NSDraggingInfo >)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - Q_UNUSED(sender); - dragEnterSequence = -1; - // drag enter event was rejected, so ignore the move event. - if (dropData) { - QDragLeaveEvent de; - QApplication::sendEvent(qwidget, &de); - [self removeDropData]; - } - [self changeDraggingCursor:NSDragOperationEvery]; - -} - -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - dragEnterSequence = -1; - [self addDropData:sender]; - - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint posDrop(localPoint.x, localPoint.y); - - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - // send the drop event to the widget. - QDropEvent de(posDrop, qtAllowed, mimeData, - QApplication::mouseButtons(), QApplication::keyboardModifiers()); - if (QDragManager::self()->object) - QDragManager::self()->dragPrivate()->target = qwidget; - QApplication::sendEvent(qwidget, &de); - if (QDragManager::self()->object) - QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); - if (!de.isAccepted()) - return NO; - else - return YES; -} - - (void)dealloc { + QMacCocoaAutoReleasePool pool; delete composingText; [[NSNotificationCenter defaultCenter] removeObserver:self]; #ifdef ALIEN_DEBUG --qCocoaViewCount; - qDebug() << "qCocoaViewCount is" << qCocoaViewCount; + qDebug() << "Alien: widget deallocated. qCocoaViewCount is:" << qCocoaViewCount; #endif [super dealloc]; @@ -529,9 +245,8 @@ static int qCocoaViewCount = 0; { if (!qwidget) return; - - // We use a different graphics system. if (QApplicationPrivate::graphicsSystem() != 0 && !qwidgetprivate->isInUnifiedToolbar) { + // INVARIANT: We use a different graphics system. // Qt handles the painting occuring inside the window. // Cocoa also keeps track of all widgets as NSView and therefore might @@ -558,7 +273,7 @@ static int qCocoaViewCount = 0; qwidgetprivate->hd = cg; // We steal the CGContext for flushing in the unified toolbar with the raster engine. - if (QApplicationPrivate::graphicsSystem() != 0 && qwidgetprivate->isInUnifiedToolbar) { + if (QApplicationPrivate::graphicsSystem() != 0 && qwidgetprivate->isInUnifiedToolbar && qwidgetprivate->unifiedSurface) { qwidgetprivate->cgContext = cg; qwidgetprivate->hasOwnContext = true; qwidgetprivate->unifiedSurface->flush(qwidget, qwidgetprivate->ut_rg, qwidgetprivate->ut_pt); @@ -574,13 +289,13 @@ static int qCocoaViewCount = 0; const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height); QRegion qrgn; - const NSRect *rects; - NSInteger count; - [self getRectsBeingDrawn:&rects count:&count]; - for (int i = 0; i < count; ++i) { - QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height); - qrgn += tmpRect; - } + const NSRect *rects; + NSInteger count; + [self getRectsBeingDrawn:&rects count:&count]; + for (int i = 0; i < count; ++i) { + QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height); + qrgn += tmpRect; + } if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) { const QRegion &parentMask = qwidget->window()->mask(); @@ -610,38 +325,27 @@ static int qCocoaViewCount = 0; } if (qwidget->isWindow() && !qwidgetprivate->isOpaque - && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) { + && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) { CGContextClearRect(cg, NSRectToCGRect(aRect)); } - // Check for alien widgets, use qwidgetPrivate->drawWidget() to draw the widget if this - // is the case. This makes sure child widgets are drawn as well, Cocoa does not know about - // those and wont send them drawRect calls. - if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false) { - if ((engine && !qwidget->testAttribute(Qt::WA_NoSystemBackground) - && (qwidget->isWindow() || qwidget->autoFillBackground())) - || qwidget->testAttribute(Qt::WA_TintedBackground) - || qwidget->testAttribute(Qt::WA_StyledBackground)) { -#ifdef DEBUG_WIDGET_PAINT - if(doDebug) - qDebug(" Handling erase for [%s::%s]", qwidget->metaObject()->className(), - qwidget->objectName().local8Bit().data()); -#endif - QPainter p(qwidget); - qwidgetprivate->paintBackground(&p, qrgn, - qwidget->isWindow() ? QWidgetPrivate::DrawAsRoot : 0); - p.end(); - } - QPaintEvent e(qrgn); -#ifdef QT3_SUPPORT - e.setErased(true); -#endif - qt_sendSpontaneousEvent(qwidget, &e); - } else { - qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); // QWidgetPrivate::drawWidget sets this - QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget); - qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen | QWidgetPrivate::DrawRecursive, 0); - } + qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); + QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget); + + // We specify that we want to draw the widget itself, and + // all its children recursive. But we skip native children, because + // they will receive drawRect calls by themselves as needed: + int flags = QWidgetPrivate::DrawPaintOnScreen + | QWidgetPrivate::DrawRecursive + | QWidgetPrivate::DontDrawNativeChildren; + + if (qwidget->isWindow()) + flags |= QWidgetPrivate::DrawAsRoot; + + // Start to draw: + qt_mac_clearDirtyOnWidgetInsideDrawWidget = true; + qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), flags, 0); + qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; if (!redirectionOffset.isNull()) QPainter::restoreRedirected(qwidget); @@ -650,7 +354,7 @@ static int qCocoaViewCount = 0; qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); if(!qwidget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && qwidget->paintingActive()) qWarning("QWidget: It is dangerous to leave painters active on a" - " widget outside of the PaintEvent"); + " widget outside of the PaintEvent"); } qwidgetprivate->hd = 0; CGContextRestoreGState(cg); @@ -659,11 +363,13 @@ static int qCocoaViewCount = 0; - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { - if (!qwidget) + // Find the widget that should receive the event: + QPoint qlocal, qglobal; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::MouseButtonPress, qlocal, qglobal, qwidget, 0); + if (!widgetToGetMouse) return NO; - Q_UNUSED(theEvent); - return !qwidget->testAttribute(Qt::WA_MacNoClickThrough); + return !widgetToGetMouse->testAttribute(Qt::WA_MacNoClickThrough); } - (NSView *)hitTest:(NSPoint)aPoint @@ -714,161 +420,119 @@ static int qCocoaViewCount = 0; - (void)mouseEntered:(NSEvent *)event { - if (!qwidget) - return; - if (qwidgetprivate->data.in_destructor) - return; - - if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) { - QEvent enterEvent(QEvent::Enter); - NSPoint windowPoint = [event locationInWindow]; - NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint]; - NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil]; - QApplication::sendEvent(qwidget, &enterEvent); - qt_mouseover = qwidget; - - // Update cursor icon: - qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint()); - - // Send mouse move and hover events as well: - if (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window()) { - // This mouse move event should be sendt, even when mouse - // tracking is switched off (to trigger tooltips): - NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSMouseMoved - location:windowPoint modifierFlags:[event modifierFlags] timestamp:[event timestamp] - windowNumber:[event windowNumber] context:[event context] eventNumber:[event eventNumber] - clickCount:0 pressure:0]; - qt_mac_handleMouseEvent(self, mouseEvent, QEvent::MouseMove, Qt::NoButton); - - if (qwidget->testAttribute(Qt::WA_Hover)) { - QHoverEvent he(QEvent::HoverEnter, QPoint(viewPoint.x, viewPoint.y), QPoint(-1, -1)); - QApplicationPrivate::instance()->notify_helper(qwidget, &he); - } - } - } + // Cocoa will not send a move event on mouseEnter. But since + // Qt expect this, we fake one now. See also mouseExited below + // for info about enter/leave event handling + NSEvent *nsmoveEvent = [NSEvent + mouseEventWithType:NSMouseMoved + location:[[self window] mouseLocationOutsideOfEventStream] + modifierFlags: [event modifierFlags] + timestamp: [event timestamp] + windowNumber: [event windowNumber] + context: [event context] + eventNumber: [event eventNumber] + clickCount: 0 + pressure: 0]; + + // Important: Cocoa sends us mouseEnter on all views under the mouse + // and not just the one on top. Therefore, to we cannot use qwidget + // as native widget for this case. Instead, we let qt_mac_handleMouseEvent + // resolve it (last argument set to 0): + qt_mac_handleMouseEvent(nsmoveEvent, QEvent::MouseMove, Qt::NoButton, 0); } - (void)mouseExited:(NSEvent *)event { - if (!qwidget) - return; - - QEvent leaveEvent(QEvent::Leave); - NSPoint globalPoint = [[event window] convertBaseToScreen:[event locationInWindow]]; - if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) { - QApplication::sendEvent(qwidget, &leaveEvent); - - // ### Think about if it is necessary to update the cursor, should only be for a few cases. - qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint()); - if (qwidget->testAttribute(Qt::WA_Hover) - && (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window())) { - QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1), - qwidget->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos)); - QApplicationPrivate::instance()->notify_helper(qwidget, &he); + // Note: normal enter/leave handling is done from within mouseMove. This handler + // catches the case when the mouse moves out of the window (which mouseMove do not). + // Updating the mouse cursor follows the same logic as enter/leave. And we update + // neither if a grab exists (even if the grab points to this widget, it seems, ref X11) + Q_UNUSED(event); + if (self == [[self window] contentView] && !qt_button_down && !QWidget::mouseGrabber()) { + qt_mac_update_cursor(); + // If the mouse exits the content view, but qt_mac_getTargetForMouseEvent still + // reports a target, it means that either there is a grab involved, or the mouse + // hovered over another window in the application. In both cases, move events will + // cause qt_mac_handleMouseEvent to be called, which will handle enter/leave. + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Leave, qlocal, qglobal, qwidget, &widgetUnderMouse); + + if (widgetUnderMouse == 0) { + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; } } } - (void)flagsChanged:(NSEvent *)theEvent { - if (!qwidget) + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) return; - QWidget *widgetToGetKey = qwidget; - - QWidget *popup = qAppInstance()->activePopupWidget(); - if (popup && popup != qwidget->window()) - widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup; qt_dispatchModifiersChanged(theEvent, widgetToGetKey); [super flagsChanged:theEvent]; } - (void)mouseMoved:(NSEvent *)theEvent { - if (!qwidget) - return; - - // We always enable mouse tracking for all QCocoaView-s. In cases where we have - // child views, we will receive mouseMoved for both parent & the child (if - // mouse is over the child). We need to ignore the parent mouseMoved in such - // cases. - NSPoint windowPoint = [theEvent locationInWindow]; - NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; - if (candidateView && candidateView == self) { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); - } + // Important: this method will only be called when the view's window is _not_ inside + // QCocoaWindow/QCocoaPanel. Otherwise, [QCocoaWindow sendEvent] will handle the event + // before it ends up here. So, this method is added for supporting QMacNativeWidget. + // TODO: Cocoa send move events to all views under the mouse. So make sure we only + // handle the event for the widget on top when using QMacNativeWidget. + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)mouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::LeftButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::LeftButton, qwidget); // Don't call super here. This prevents us from getting the mouseUp event, // which we need to send even if the mouseDown event was not accepted. // (this is standard Qt behavior.) } - - (void)mouseUp:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::LeftButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::LeftButton, qwidget); } - (void)rightMouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::RightButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::RightButton, qwidget); } - (void)rightMouseUp:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::RightButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::RightButton, qwidget); } - (void)otherMouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, mouseButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, mouseButton, qwidget); } - (void)otherMouseUp:(NSEvent *)theEvent { Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, mouseButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, mouseButton, qwidget); } - (void)mouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)rightMouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)otherMouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)scrollWheel:(NSEvent *)theEvent @@ -879,21 +543,14 @@ static int qCocoaViewCount = 0; [currentIManager handleMouseEvent:theEvent]; } - NSPoint windowPoint = [theEvent locationInWindow]; - NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint]; - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint qlocal = QPoint(localPoint.x, localPoint.y); - QPoint qglobal = QPoint(globalPoint.x, flipYCoordinate(globalPoint.y)); Qt::MouseButtons buttons = QApplication::mouseButtons(); - bool wheelOK = false; Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]); - QWidget *widgetToGetMouse = qwidget; - // if popup is open it should get wheel events if the cursor is over the popup, - // otherwise the event should be ignored. - if (QWidget *popup = qAppInstance()->activePopupWidget()) { - if (!popup->geometry().contains(qglobal)) - return; - } + + // Find the widget that should receive the event: + QPoint qlocal, qglobal; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::Wheel, qlocal, qglobal, qwidget, 0); + if (!widgetToGetMouse) + return; int deltaX = 0; int deltaY = 0; @@ -922,46 +579,30 @@ static int qCocoaViewCount = 0; } #ifndef QT_NO_WHEELEVENT + // ### Qt 5: Send one QWheelEvent with dx, dy and dz + + if (deltaX != 0 && deltaY != 0) + QMacScrollOptimization::initDelayedScroll(); + if (deltaX != 0) { QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal); qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaX, buttons, keyMods, Qt::Horizontal); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } } - if (deltaY) { + if (deltaY != 0) { QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical); qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaY, buttons, keyMods, Qt::Vertical); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } } - if (deltaZ) { + if (deltaZ != 0) { // Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to // try to be ahead of the pack, I'm adding this extra value. QWheelEvent qwe(qlocal, qglobal, deltaZ, buttons, keyMods, (Qt::Orientation)3); qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaZ, buttons, keyMods, (Qt::Orientation)3); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } } + + if (deltaX != 0 && deltaY != 0) + QMacScrollOptimization::performDelayedScroll(); #endif //QT_NO_WHEELEVENT } @@ -976,35 +617,14 @@ static int qCocoaViewCount = 0; [super tabletPoint:tabletEvent]; } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -- (void)touchesBeganWithEvent:(NSEvent *)event -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesMovedWithEvent:(NSEvent *)event -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesEndedWithEvent:(NSEvent *)event -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesCancelledWithEvent:(NSEvent *)event -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - - (void)magnifyWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1013,13 +633,18 @@ static int qCocoaViewCount = 0; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); qNGEvent.percentage = [event magnification]; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } - (void)rotateWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1028,13 +653,18 @@ static int qCocoaViewCount = 0; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); qNGEvent.percentage = -[event rotation]; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } - (void)swipeWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1050,13 +680,18 @@ static int qCocoaViewCount = 0; qNGEvent.angle = 90.0f; else if ([event deltaY] == -1) qNGEvent.angle = 270.0f; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } - (void)beginGestureWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1064,13 +699,18 @@ static int qCocoaViewCount = 0; qNGEvent.gestureType = QNativeGestureEvent::GestureBegin; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } - (void)endGestureWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1078,9 +718,9 @@ static int qCocoaViewCount = 0; qNGEvent.gestureType = QNativeGestureEvent::GestureEnd; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); - qt_sendSpontaneousEvent(qwidget, &qNGEvent); -#endif // QT_NO_GESTURES + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); } +#endif // QT_NO_GESTURES - (void)frameDidChange:(NSNotification *)note { @@ -1123,16 +763,32 @@ static int qCocoaViewCount = 0; { if (!qwidget) return NO; - // disabled widget shouldn't get focus even if it's a window. + + // Disabled widget shouldn't get focus even if it's a window. // hence disabled windows will not get any key or mouse events. if (!qwidget->isEnabled()) return NO; - // Before accepting the focus for a window, we check that - // the focusWidget (if any) is not contained in the same window. - if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded - && (!qApp->focusWidget() || qApp->focusWidget()->window() != qwidget)) { - return YES; // Always do it, so that windows can accept key press events. + + if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded) { + QWidget *focusWidget = qApp->focusWidget(); + if (!focusWidget) { + // There is no focus widget, but we still want to receive key events + // for shortcut handling etc. So we accept first responer for the + // content view as a last resort: + return YES; + } + if (!focusWidget->internalWinId() && focusWidget->nativeParentWidget() == qwidget) { + // The current focus widget is alien, and hence, cannot get acceptsFirstResponder + // calls. Since the focus widget is a child of qwidget, we let this view say YES: + return YES; + } + if (focusWidget->window() != qwidget) { + // The current focus widget is in another window. Since cocoa + // suggest that this window should be key now, we accept: + return YES; + } } + return qwidget->focusPolicy() != Qt::NoFocus; } @@ -1140,10 +796,13 @@ static int qCocoaViewCount = 0; { if (!qwidget) return NO; + // Seems like the following test only triggers if this // view is inside a QMacNativeWidget: - if (qwidget == QApplication::focusWidget()) - qwidget->clearFocus(); +// if (QWidget *fw = QApplication::focusWidget()) { +// if (qwidget == fw || qwidget == fw->nativeParentWidget()) +// fw->clearFocus(); +// } return YES; } @@ -1172,11 +831,11 @@ static int qCocoaViewCount = 0; { Q_UNUSED(anImage); Q_UNUSED(aPoint); - qMacDnDParams()->performedAction = operation; + macCurrentDnDParameters()->performedAction = operation; if (QDragManager::self()->object && QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) { - qMacDnDParams()->performedAction = - qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action); + macCurrentDnDParameters()->performedAction = + qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action); } } @@ -1191,51 +850,37 @@ static int qCocoaViewCount = 0; qwidgetprivate = 0; } -- (BOOL)qt_leftButtonIsRightButton -{ - return leftButtonIsRightButton; -} - -- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped -{ - leftButtonIsRightButton = isSwapped; -} - -+ (DnDParams*)currentMouseEvent -{ - return qMacDnDParams(); -} - - (void)keyDown:(NSEvent *)theEvent { - sendKeyEvents = true; - - QWidget *widgetToGetKey = qwidget; + if (!qwidget) + return; + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; - QWidget *popup = qAppInstance()->activePopupWidget(); - bool sendToPopup = false; - if (popup && popup != qwidget->window()) { - widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup; - sendToPopup = true; - } + sendKeyEvents = true; if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled) && !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly || widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly || widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) { fromKeyDownEvent = true; - [qt_mac_nativeview_for(widgetToGetKey) interpretKeyEvents:[NSArray arrayWithObject: theEvent]]; + [qt_mac_nativeview_for(qwidget) interpretKeyEvents:[NSArray arrayWithObject: theEvent]]; fromKeyDownEvent = false; } + if (sendKeyEvents && !composing) { - bool keyOK = qt_dispatchKeyEvent(theEvent, widgetToGetKey); - if (!keyOK && !sendToPopup) { - // find the first responder that is not created by Qt and forward - // the event to it (for example if Qt widget is embedded into native). + bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); + if (!keyEventEaten && qwidget) { + // The event is not yet eaten, and if Qt is embedded inside a native + // cocoa application, send it to first responder not owned by Qt. + // The exception is if widgetToGetKey was redirected to a popup. QWidget *toplevel = qwidget->window(); - if (toplevel && qt_widget_private(toplevel)->topData()->embedded) { - if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) - [w keyDown:theEvent]; + if (toplevel == widgetToGetKey->window()) { + if (qt_widget_private(toplevel)->topData()->embedded) { + if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) + [w keyDown:theEvent]; + } } } } @@ -1245,12 +890,21 @@ static int qCocoaViewCount = 0; - (void)keyUp:(NSEvent *)theEvent { if (sendKeyEvents) { - bool keyOK = qt_dispatchKeyEvent(theEvent, qwidget); - if (!keyOK) { + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; + + bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); + if (!keyEventEaten && qwidget) { + // The event is not yet eaten, and if Qt is embedded inside a native + // cocoa application, send it to first responder not owned by Qt. + // The exception is if widgetToGetKey was redirected to a popup. QWidget *toplevel = qwidget->window(); - if (toplevel && qt_widget_private(toplevel)->topData()->embedded) { - if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) - [w keyUp:theEvent]; + if (toplevel == widgetToGetKey->window()) { + if (qt_widget_private(toplevel)->topData()->embedded) { + if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) + [w keyUp:theEvent]; + } } } } @@ -1298,13 +952,14 @@ static int qCocoaViewCount = 0; // When entering characters through Character Viewer or Keyboard Viewer, the text is passed // through this insertText method. Since we dont receive a keyDown Event in such cases, the // composing flag will be false. - if (([aString length] && composing) || !fromKeyDownEvent) { + if (([aString length] && composing) || !fromKeyDownEvent) { // Send the commit string to the widget. composing = false; sendKeyEvents = false; QInputMethodEvent e; e.setCommitString(commitText); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); } else { // The key sequence "`q" on a French Keyboard will generate two calls to insertText before // it returns from interpretKeyEvents. The first call will turn off 'composing' and accept @@ -1368,8 +1023,11 @@ static int qCocoaViewCount = 0; 0, composingLength, format); } *composingText = qtText; + QInputMethodEvent e(qtText, attrs); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); + if (!composingLength) composing = false; } @@ -1379,7 +1037,8 @@ static int qCocoaViewCount = 0; if (composing) { QInputMethodEvent e; e.setCommitString(*composingText); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); } composingText->clear(); composing = false; @@ -1452,8 +1111,12 @@ static int qCocoaViewCount = 0; { Q_UNUSED(theRange); // The returned rect is always based on the internal cursor. - QRect mr(qwidget->inputMethodQuery(Qt::ImMicroFocus).toRect()); - QPoint mp(qwidget->mapToGlobal(QPoint(mr.bottomLeft()))); + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return NSZeroRect; + + QRect mr(widgetToGetKey->inputMethodQuery(Qt::ImMicroFocus).toRect()); + QPoint mp(widgetToGetKey->mapToGlobal(QPoint(mr.bottomLeft()))); NSRect rect ; rect.origin.x = mp.x(); rect.origin.y = flipYCoordinate(mp.y()); @@ -1471,10 +1134,11 @@ static int qCocoaViewCount = 0; - (NSArray*) validAttributesForMarkedText { - if (qwidget == 0) + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) return nil; - if (!qwidget->testAttribute(Qt::WA_InputMethodEnabled)) + if (!widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled)) return nil; // Not sure if that's correct, but it's saves a malloc. // Support only underline color/style. @@ -1488,7 +1152,7 @@ void QMacInputContext::reset() { QWidget *w = QInputContext::focusWidget(); if (w) { - NSView *view = qt_mac_nativeview_for(w); + NSView *view = qt_mac_effectiveview_for(w); if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) { QMacCocoaAutoReleasePool pool; QT_MANGLE_NAMESPACE(QCocoaView) *qc = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view); @@ -1505,7 +1169,7 @@ bool QMacInputContext::isComposing() const { QWidget *w = QInputContext::focusWidget(); if (w) { - NSView *view = qt_mac_nativeview_for(w); + NSView *view = qt_mac_effectiveview_for(w); if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) { return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) isComposing]; } @@ -1598,38 +1262,45 @@ Qt::DropAction QDragManager::drag(QDrag *o) } else { hotspot = dragPrivate()->hotspot; } - // convert the image to NSImage. + + // Convert the image to NSImage: NSImage *image = (NSImage *)qt_mac_create_nsimage(pix); [image retain]; - DnDParams dndParams = *[QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; - // save supported actions - [dndParams.view setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)]; - NSPoint imageLoc = {dndParams.localPoint.x - hotspot.x(), - dndParams.localPoint.y + pix.height() - hotspot.y()}; + + DnDParams *dndParams = macCurrentDnDParameters(); + QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(dndParams->view); + + // Save supported actions: + [theView setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)]; + QPoint pointInView = [theView qt_qwidget]->mapFromGlobal(dndParams->globalPoint); + NSPoint imageLoc = {pointInView.x() - hotspot.x(), pointInView.y() + pix.height() - hotspot.y()}; NSSize mouseOffset = {0.0, 0.0}; NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; dragPrivate()->executed_action = Qt::ActionMask; - // do the drag - [dndParams.view retain]; - [dndParams.view dragImage:image - at:imageLoc - offset:mouseOffset - event:dndParams.theEvent - pasteboard:pboard - source:dndParams.view - slideBack:YES]; - // reset the implicit grab widget when drag ends because we will not - // receive the mouse release event when DND is active. + + // Execute the drag: + [theView retain]; + [theView dragImage:image + at:imageLoc + offset:mouseOffset + event:dndParams->theEvent + pasteboard:pboard + source:theView + slideBack:YES]; + + // Reset the implicit grab widget when drag ends because we will not + // receive the mouse release event when DND is active: qt_button_down = 0; - [dndParams.view release]; + [theView release]; [image release]; if (dragPrivate()) dragPrivate()->executed_action = Qt::IgnoreAction; object = 0; - Qt::DropAction performedAction(qt_mac_mapNSDragOperation(qMacDnDParams()->performedAction)); - // do post drag processing, if required. - if(performedAction != Qt::IgnoreAction) { - // check if the receiver points us to a file location. + Qt::DropAction performedAction(qt_mac_mapNSDragOperation(dndParams->performedAction)); + + // Do post drag processing, if required. + if (performedAction != Qt::IgnoreAction) { + // Check if the receiver points us to a file location. // if so, we need to do the file copy/move ourselves. QCFType<CFURLRef> pasteLocation = 0; PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation); @@ -1646,6 +1317,8 @@ Qt::DropAction QDragManager::drag(QDrag *o) } } } + + // Clean-up: o->setMimeData(0); o->deleteLater(); return performedAction; diff --git a/src/gui/kernel/qcocoaview_mac_p.h b/src/gui/kernel/qcocoaview_mac_p.h index 8d57f95..cc79b67 100644 --- a/src/gui/kernel/qcocoaview_mac_p.h +++ b/src/gui/kernel/qcocoaview_mac_p.h @@ -58,57 +58,30 @@ QT_FORWARD_DECLARE_CLASS(QWidgetPrivate); QT_FORWARD_DECLARE_CLASS(QWidget); QT_FORWARD_DECLARE_CLASS(QEvent); -QT_FORWARD_DECLARE_CLASS(QCocoaDropData); QT_FORWARD_DECLARE_CLASS(QString); QT_FORWARD_DECLARE_CLASS(QStringList); -QT_BEGIN_NAMESPACE -struct DnDParams -{ - QT_MANGLE_NAMESPACE(QCocoaView) *view; - NSEvent *theEvent; - NSPoint localPoint; - NSDragOperation performedAction; - NSPoint activeDragEnterPos; -}; - -QT_END_NAMESPACE - -QT_FORWARD_DECLARE_STRUCT(DnDParams); - Q_GUI_EXPORT @interface QT_MANGLE_NAMESPACE(QCocoaView) : NSControl <NSTextInput> { QWidget *qwidget; QWidgetPrivate *qwidgetprivate; - bool leftButtonIsRightButton; - QCocoaDropData *dropData; NSDragOperation supportedActions; bool composing; int composingLength; bool sendKeyEvents; bool fromKeyDownEvent; QString *composingText; - NSInteger dragEnterSequence; + @public int alienTouchCount; } - (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; - (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; - (void)frameDidChange:(NSNotification *)note; -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender; -- (void)draggingExited:(id < NSDraggingInfo >)sender; -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; -- (void)removeDropData; -- (void)addDropData:(id <NSDraggingInfo>)sender; - (void)setSupportedActions:(NSDragOperation)actions; - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; - (BOOL)isComposing; - (QWidget *)qt_qwidget; - (void) qt_clearQWidget; -- (BOOL)qt_leftButtonIsRightButton; -- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped; -- (void)changeDraggingCursor:(NSDragOperation)newOperation; -+ (DnDParams*)currentMouseEvent; @end #endif diff --git a/src/gui/kernel/qcocoawindow_mac.mm b/src/gui/kernel/qcocoawindow_mac.mm index 28f09ad..6e5023a 100644 --- a/src/gui/kernel/qcocoawindow_mac.mm +++ b/src/gui/kernel/qcocoawindow_mac.mm @@ -47,6 +47,8 @@ #import <private/qt_cocoa_helpers_mac_p.h> #import <private/qcocoawindowcustomthemeframe_mac_p.h> #import <private/qcocoaapplication_mac_p.h> +#import <private/qdnd_p.h> +#import <private/qmultitouch_mac_p.h> #include <QtGui/QWidget> diff --git a/src/gui/kernel/qcocoawindow_mac_p.h b/src/gui/kernel/qcocoawindow_mac_p.h index 81e0be1..e6b50f5 100644 --- a/src/gui/kernel/qcocoawindow_mac_p.h +++ b/src/gui/kernel/qcocoawindow_mac_p.h @@ -50,17 +50,20 @@ // We mean it. // +#ifndef QCOCOAWINDOW_MAC_P +#define QCOCOAWINDOW_MAC_P + #ifdef QT_MAC_USE_COCOA #include "qmacdefines_mac.h" #import <Cocoa/Cocoa.h> #include <private/qapplication_p.h> #include <private/qbackingstore_p.h> - enum { QtMacCustomizeWindow = 1 << 21 }; // This will one day be run over by QT_FORWARD_DECLARE_CLASS(QWidget); QT_FORWARD_DECLARE_CLASS(QStringList); +QT_FORWARD_DECLARE_CLASS(QCocoaDropData); @interface NSWindow (QtCoverForHackWithCategory) + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; @@ -72,13 +75,16 @@ QT_FORWARD_DECLARE_CLASS(QStringList); @end @interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow { - bool leftButtonIsRightButton; QStringList *currentCustomDragTypes; + QCocoaDropData *dropData; + NSInteger dragEnterSequence; } + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; - (void)registerDragTypes; +- (void)drawRectOriginal:(NSRect)rect; @end #endif +#endif diff --git a/src/gui/kernel/qcursor.h b/src/gui/kernel/qcursor.h index d90375da..c993763 100644 --- a/src/gui/kernel/qcursor.h +++ b/src/gui/kernel/qcursor.h @@ -77,7 +77,7 @@ class QBitmap; class QPixmap; #if defined(Q_WS_MAC) -void qt_mac_set_cursor(const QCursor *c, const QPoint &p); +void qt_mac_set_cursor(const QCursor *c); #endif #if defined(Q_OS_SYMBIAN) extern void qt_symbian_show_pointer_sprite(); @@ -141,7 +141,8 @@ private: QCursorData *d; #if defined(Q_WS_MAC) friend void *qt_mac_nsCursorForQCursor(const QCursor &c); - friend void qt_mac_set_cursor(const QCursor *c, const QPoint &p); + friend void qt_mac_set_cursor(const QCursor *c); + friend void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); #endif #if defined(Q_OS_SYMBIAN) friend void qt_symbian_show_pointer_sprite(); diff --git a/src/gui/kernel/qcursor_mac.mm b/src/gui/kernel/qcursor_mac.mm index 1a82a68..0d57b85 100644 --- a/src/gui/kernel/qcursor_mac.mm +++ b/src/gui/kernel/qcursor_mac.mm @@ -50,6 +50,7 @@ #include <AppKit/NSCursor.h> #include <qpainter.h> #include <private/qt_cocoa_helpers_mac_p.h> +#include <private/qapplication_p.h> QT_BEGIN_NAMESPACE @@ -60,6 +61,7 @@ extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp extern GrafPtr qt_mac_qd_context(const QPaintDevice *); //qpaintdevice_mac.cpp extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_mac.cpp +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp /***************************************************************************** Internal QCursorData class @@ -95,18 +97,19 @@ protected: } }; -void *qt_mac_nsCursorForQCursor(const QCursor &c) +inline void *qt_mac_nsCursorForQCursor(const QCursor &c) { c.d->update(); return [[static_cast<NSCursor *>(c.d->curs.cp.nscursor) retain] autorelease]; } static QCursorData *currentCursor = 0; //current cursor -void qt_mac_set_cursor(const QCursor *c, const QPoint &) + +void qt_mac_set_cursor(const QCursor *c) { #ifdef QT_MAC_USE_COCOA - Q_UNUSED(c); - return; + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*c)) set]; #else if (!c) { currentCursor = 0; @@ -128,35 +131,122 @@ void qt_mac_set_cursor(const QCursor *c, const QPoint &) c->d->curs.tc.anim->start(c->d->curs.tc.curs); } } + currentCursor = c->d; #endif } -void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) +static QPointer<QWidget> lastWidgetUnderMouse = 0; +static QPointer<QWidget> lastMouseCursorWidget = 0; +static bool qt_button_down_on_prev_call = false; +static QCursor *grabCursor = 0; + +void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse) { -#ifdef QT_MAC_USE_COCOA - Q_UNUSED(globalPos); - return; -#else QCursor cursor(Qt::ArrowCursor); + if (qt_button_down) { + // The widget that is currently pressed + // grabs the mouse cursor: + widgetUnderMouse = qt_button_down; + qt_button_down_on_prev_call = true; + } else if (qt_button_down_on_prev_call) { + // Grab has been released, so do + // a full check: + qt_button_down_on_prev_call = false; + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + } + if (QApplication::overrideCursor()) { cursor = *QApplication::overrideCursor(); - } else { - for(QWidget *w = QApplication::widgetAt(globalPos); w; w = w->parentWidget()) { - if(w->testAttribute(Qt::WA_SetCursor)) { - cursor = w->cursor(); - break; + } else if (grabCursor) { + cursor = *grabCursor; + } else if (widgetUnderMouse) { + if (widgetUnderMouse == lastWidgetUnderMouse) { + // Optimization that should hit when the widget under + // the mouse does not change as the mouse moves: + if (lastMouseCursorWidget) + cursor = lastMouseCursorWidget->cursor(); + } else { + QWidget *w = widgetUnderMouse; + for (; w; w = w->parentWidget()) { + if (w->testAttribute(Qt::WA_SetCursor)) { + cursor = w->cursor(); + break; + } + if (w->isWindow()) + break; } + // One final check in case we ran out of parents in the loop: + if (w && !w->testAttribute(Qt::WA_SetCursor)) + w = 0; + + lastWidgetUnderMouse = widgetUnderMouse; + lastMouseCursorWidget = w; } } - qt_mac_set_cursor(&cursor, globalPos); + +#ifdef QT_MAC_USE_COCOA + cursor.d->update(); + NSCursor *nsCursor = static_cast<NSCursor *>(cursor.d->curs.cp.nscursor); + if ([NSCursor currentCursor] != nsCursor) { + QMacCocoaAutoReleasePool pool; + [nsCursor set]; + } +#else + qt_mac_set_cursor(&cursor); #endif } void qt_mac_update_cursor() { - qt_mac_update_cursor_at_global_pos(QCursor::pos()); + // This function is similar to qt_mac_updateCursorWithWidgetUnderMouse + // except that is clears the optimization cache, and finds the widget + // under mouse itself. Clearing the cache is useful in cases where the + // application has been deactivated/activated etc. + // NB: since we dont have any true native widget, the call to + // qt_mac_getTargetForMouseEvent will fail when the mouse is over QMacNativeWidgets. +#ifdef QT_MAC_USE_COCOA + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + QWidget *widgetUnderMouse = 0; + + if (qt_button_down) { + widgetUnderMouse = qt_button_down; + } else { + QPoint localPoint; + QPoint globalPoint = QCursor::pos(); + qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, 0, &widgetUnderMouse); + } + qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); +#else + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(QCursor::pos())); +#endif +} + +void qt_mac_setMouseGrabCursor(bool set, QCursor *const cursor = 0) +{ + if (grabCursor) { + delete grabCursor; + grabCursor = 0; + } + if (set) { + if (cursor) + grabCursor = new QCursor(*cursor); + else if (lastMouseCursorWidget) + grabCursor = new QCursor(lastMouseCursorWidget->cursor()); + else + grabCursor = new QCursor(Qt::ArrowCursor); + } + qt_mac_update_cursor(); +} + +#ifndef QT_MAC_USE_COCOA +void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) +{ + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(globalPos)); } +#endif static int nextCursorId = Qt::BitmapCursor; @@ -427,7 +517,8 @@ void QCursorData::update() break; case Qt::DragCopyCursor: type = QCursorData::TYPE_ThemeCursor; - curs.cp.nscursor = [NSCursor dragCopyCursor]; + if ([NSCursor respondsToSelector:@selector(dragCopyCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragCopyCursor)]; break; case Qt::DragMoveCursor: type = QCursorData::TYPE_ThemeCursor; @@ -435,7 +526,8 @@ void QCursorData::update() break; case Qt::DragLinkCursor: type = QCursorData::TYPE_ThemeCursor; - curs.cp.nscursor = [NSCursor dragLinkCursor]; + if ([NSCursor respondsToSelector:@selector(dragLinkCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragLinkCursor)]; break; #define QT_USE_APPROXIMATE_CURSORS #ifdef QT_USE_APPROXIMATE_CURSORS diff --git a/src/gui/kernel/qdesktopwidget_qpa_p.h b/src/gui/kernel/qdesktopwidget_qpa_p.h index 8bed09d..47ccca2 100644 --- a/src/gui/kernel/qdesktopwidget_qpa_p.h +++ b/src/gui/kernel/qdesktopwidget_qpa_p.h @@ -59,7 +59,13 @@ class QDesktopScreenWidget : public QWidget { Q_OBJECT public: - QDesktopScreenWidget(int screenNumber = -1) { setWindowFlags(Qt::Desktop); setVisible(false); d_func()->screenNumber = screenNumber; } + QDesktopScreenWidget(int screenNumber = -1) + { + setWindowFlags(Qt::Desktop); + setVisible(false); + QTLWExtra *topData = d_func()->topData(); + topData->screenIndex = screenNumber; + } }; class QDesktopWidgetPrivate : public QWidgetPrivate { diff --git a/src/gui/kernel/qdnd_mac.mm b/src/gui/kernel/qdnd_mac.mm index d630faa..3af2ba0 100644 --- a/src/gui/kernel/qdnd_mac.mm +++ b/src/gui/kernel/qdnd_mac.mm @@ -491,7 +491,7 @@ bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef) SetThemeCursor(cursor); } if(found_cursor) { - qt_mac_set_cursor(0, QPoint()); //just use our's + qt_mac_set_cursor(0); //just use our's } else { QCursor cursor(Qt::ArrowCursor); if(qApp && qApp->overrideCursor()) { @@ -504,7 +504,7 @@ bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef) } } } - qt_mac_set_cursor(&cursor, QPoint(mouse.h, mouse.v)); + qt_mac_set_cursor(&cursor); } //idle things diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h index 9a55b18..7543666 100644 --- a/src/gui/kernel/qdnd_p.h +++ b/src/gui/kernel/qdnd_p.h @@ -76,7 +76,7 @@ class QEventLoop; #if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD)) -class QInternalMimeData : public QMimeData +class Q_GUI_EXPORT QInternalMimeData : public QMimeData { Q_OBJECT public: diff --git a/src/gui/kernel/qkeymapper_x11_p.cpp b/src/gui/kernel/qkeymapper_x11_p.cpp index 1fad4b0..2dbe1e7 100644 --- a/src/gui/kernel/qkeymapper_x11_p.cpp +++ b/src/gui/kernel/qkeymapper_x11_p.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ // This file is auto-generated, do not edit! +// (Generated using util/xkbdatagen) static struct { const char *layout; diff --git a/src/gui/kernel/qmacgesturerecognizer_mac.mm b/src/gui/kernel/qmacgesturerecognizer_mac.mm index 0e432f3..6a4f0bb 100644 --- a/src/gui/kernel/qmacgesturerecognizer_mac.mm +++ b/src/gui/kernel/qmacgesturerecognizer_mac.mm @@ -69,6 +69,7 @@ QMacSwipeGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e case QNativeGestureEvent::Swipe: { QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture); g->setSwipeAngle(ev->angle); + g->setHotSpot(ev->position); return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; break; } default: @@ -110,6 +111,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setCenterPoint(g->startCenterPoint()); g->setChangeFlags(QPinchGesture::CenterPointChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::MayBeGesture | QGestureRecognizer::ConsumeEventHint; case QNativeGestureEvent::Rotate: { g->setLastScaleFactor(g->scaleFactor()); @@ -117,6 +119,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setRotationAngle(g->rotationAngle() + ev->percentage); g->setChangeFlags(QPinchGesture::RotationAngleChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; } case QNativeGestureEvent::Zoom: @@ -125,6 +128,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setScaleFactor(g->scaleFactor() * (1 + ev->percentage)); g->setChangeFlags(QPinchGesture::ScaleFactorChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; case QNativeGestureEvent::GestureEnd: return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; @@ -221,6 +225,7 @@ QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent * const QPointF posOffset = p - _startPos; g->setLastOffset(g->offset()); g->setOffset(QPointF(posOffset.x(), posOffset.y())); + g->setHotSpot(_startPos); return QGestureRecognizer::TriggerGesture; } } else if (_panTimer.isActive()) { @@ -239,6 +244,7 @@ QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent * break; // Begin new pan session! _startPos = QCursor::pos(); + g->setHotSpot(_startPos); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; } break; } diff --git a/src/gui/kernel/qplatformclipboard_qpa.cpp b/src/gui/kernel/qplatformclipboard_qpa.cpp new file mode 100644 index 0000000..fff4e19 --- /dev/null +++ b/src/gui/kernel/qplatformclipboard_qpa.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qplatformclipboard_qpa.h" + +QT_BEGIN_NAMESPACE + +class QClipboardData +{ +public: + QClipboardData(); + ~QClipboardData(); + + void setSource(QMimeData* s) + { + if (s == src) + return; + delete src; + src = s; + } + QMimeData* source() + { return src; } + +private: + QMimeData* src; +}; + +QClipboardData::QClipboardData() +{ + src = 0; +} + +QClipboardData::~QClipboardData() +{ + delete src; +} + +Q_GLOBAL_STATIC(QClipboardData,q_clipboardData); + +QPlatformClipboard::~QPlatformClipboard() +{ + +} + +const QMimeData *QPlatformClipboard::mimeData(QClipboard::Mode mode) const +{ + //we know its clipboard + Q_UNUSED(mode); + return q_clipboardData()->source(); +} + +void QPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + //we know its clipboard + Q_UNUSED(mode); + q_clipboardData()->setSource(data); +} + +bool QPlatformClipboard::supportsMode(QClipboard::Mode mode) const +{ + return mode == QClipboard::Clipboard; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformclipboard_qpa.h b/src/gui/kernel/qplatformclipboard_qpa.h new file mode 100644 index 0000000..3f7bfbb --- /dev/null +++ b/src/gui/kernel/qplatformclipboard_qpa.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMCLIPBOARD_QPA_H +#define QPLATFORMCLIPBOARD_QPA_H + +#include <qplatformdefs.h> + +#include <QtGui/QClipboard> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QPlatformClipboard +{ +public: + virtual ~QPlatformClipboard(); + + virtual const QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard ) const; + virtual void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard); + virtual bool supportsMode(QClipboard::Mode mode) const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif //QPLATFORMCLIPBOARD_QPA_H diff --git a/src/gui/kernel/qplatformglcontext_qpa.cpp b/src/gui/kernel/qplatformglcontext_qpa.cpp index 14bd718..1673aed 100644 --- a/src/gui/kernel/qplatformglcontext_qpa.cpp +++ b/src/gui/kernel/qplatformglcontext_qpa.cpp @@ -138,7 +138,7 @@ void QPlatformGLContext::setDefaultSharedContext(QPlatformGLContext *sharedConte Default shared context is intended to be a globally awailable pointer to a context which can be used for sharing resources when creating new contexts. Its default value is 0; */ -const QPlatformGLContext *QPlatformGLContext::defaultSharedContext() +QPlatformGLContext *QPlatformGLContext::defaultSharedContext() { return QPlatformGLContextPrivate::staticSharedContext; } diff --git a/src/gui/kernel/qplatformglcontext_qpa.h b/src/gui/kernel/qplatformglcontext_qpa.h index a70e046..807ed3d 100644 --- a/src/gui/kernel/qplatformglcontext_qpa.h +++ b/src/gui/kernel/qplatformglcontext_qpa.h @@ -69,7 +69,7 @@ public: virtual QPlatformWindowFormat platformWindowFormat() const = 0; const static QPlatformGLContext *currentContext(); - const static QPlatformGLContext *defaultSharedContext(); + static QPlatformGLContext *defaultSharedContext(); protected: diff --git a/src/gui/kernel/qplatformintegration_qpa.cpp b/src/gui/kernel/qplatformintegration_qpa.cpp index 9b6e590..0cac57d 100644 --- a/src/gui/kernel/qplatformintegration_qpa.cpp +++ b/src/gui/kernel/qplatformintegration_qpa.cpp @@ -42,6 +42,7 @@ #include "qplatformintegration_qpa.h" #include <QtGui/QPlatformFontDatabase> +#include <QtGui/QPlatformClipboard> QT_BEGIN_NAMESPACE @@ -94,6 +95,23 @@ QPlatformFontDatabase *QPlatformIntegration::fontDatabase() const } /*! + Accessor for the platform integrations clipboard. + + Default implementation returns a default QPlatformClipboard. + + \sa QPlatformClipboard + +*/ +QPlatformClipboard *QPlatformIntegration::clipboard() const +{ + static QPlatformClipboard *clipboard = 0; + if (!clipboard) { + clipboard = new QPlatformClipboard; + } + return clipboard; +} + +/*! \class QPlatformIntegration \since 4.8 \internal diff --git a/src/gui/kernel/qplatformintegration_qpa.h b/src/gui/kernel/qplatformintegration_qpa.h index f01b4f4..7050245 100644 --- a/src/gui/kernel/qplatformintegration_qpa.h +++ b/src/gui/kernel/qplatformintegration_qpa.h @@ -59,6 +59,7 @@ class QBlittable; class QWidget; class QPlatformEventLoopIntegration; class QPlatformFontDatabase; +class QPlatformClipboard; class Q_GUI_EXPORT QPlatformIntegration { @@ -76,8 +77,9 @@ public: virtual bool isVirtualDesktop() { return false; } virtual QPixmap grabWindow(WId window, int x, int y, int width, int height) const; -//Fontdatabase integration. +//Deeper window system integrations virtual QPlatformFontDatabase *fontDatabase() const; + virtual QPlatformClipboard *clipboard() const; // Experimental in mainthread eventloop integration // This should only be used if it is only possible to do window system event processing in diff --git a/src/gui/kernel/qplatformintegrationfactory_qpa.cpp b/src/gui/kernel/qplatformintegrationfactory_qpa.cpp index 9122e1a..17a130d 100644 --- a/src/gui/kernel/qplatformintegrationfactory_qpa.cpp +++ b/src/gui/kernel/qplatformintegrationfactory_qpa.cpp @@ -52,15 +52,27 @@ QT_BEGIN_NAMESPACE #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QPlatformIntegrationFactoryInterface_iid, QLatin1String("/platforms"), Qt::CaseInsensitive)) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, + (QPlatformIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) #endif -QPlatformIntegration *QPlatformIntegrationFactory::create(const QString& key) +QPlatformIntegration *QPlatformIntegrationFactory::create(const QString& key, const QString &platformPluginPath) { QPlatformIntegration *ret = 0; QStringList paramList = key.split(QLatin1Char(':')); QString platform = paramList.takeFirst().toLower(); #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + // Try loading the plugin from platformPluginPath first: + if (!platformPluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(platformPluginPath); + if (QPlatformIntegrationFactoryInterface *factory = + qobject_cast<QPlatformIntegrationFactoryInterface*>(directLoader()->instance(platform))) + ret = factory->create(key, paramList); + + if (ret) + return ret; + } if (QPlatformIntegrationFactoryInterface *factory = qobject_cast<QPlatformIntegrationFactoryInterface*>(loader()->instance(platform))) ret = factory->create(platform, paramList); #endif @@ -74,10 +86,19 @@ QPlatformIntegration *QPlatformIntegrationFactory::create(const QString& key) \sa create() */ -QStringList QPlatformIntegrationFactory::keys() +QStringList QPlatformIntegrationFactory::keys(const QString &platformPluginPath) { #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) - QStringList list = loader()->keys(); + QStringList list; + + if (!platformPluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(platformPluginPath); + foreach (const QString &key, directLoader()->keys()) { + list += key + QString(QLatin1String(" (from %1)")).arg(platformPluginPath); + } + } + + list += loader()->keys(); #else QStringList list; #endif diff --git a/src/gui/kernel/qplatformintegrationfactory_qpa_p.h b/src/gui/kernel/qplatformintegrationfactory_qpa_p.h index ba02d2c..77e1da1 100644 --- a/src/gui/kernel/qplatformintegrationfactory_qpa_p.h +++ b/src/gui/kernel/qplatformintegrationfactory_qpa_p.h @@ -66,8 +66,8 @@ class QPlatformIntegration; class QPlatformIntegrationFactory { public: - static QStringList keys(); - static QPlatformIntegration *create(const QString&); + static QStringList keys(const QString &platformPluginPath = QString()); + static QPlatformIntegration *create(const QString &key, const QString &platformPluginPath = QString()); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformscreen_qpa.cpp b/src/gui/kernel/qplatformscreen_qpa.cpp index 5e80ba8..118835f 100644 --- a/src/gui/kernel/qplatformscreen_qpa.cpp +++ b/src/gui/kernel/qplatformscreen_qpa.cpp @@ -41,7 +41,11 @@ #include "qplatformscreen_qpa.h" #include <QtGui/qapplication.h> +#include <QtGui/private/qapplication_p.h> #include <QtGui/qdesktopwidget.h> +#include <QtGui/qplatformintegration_qpa.h> +#include <QtGui/qwidget.h> +#include <QtGui/private/qwidget_p.h> /*! Return the given top level widget for a given position. @@ -77,6 +81,17 @@ QSize QPlatformScreen::physicalSize() const return QSize(width,height); } +Q_GUI_EXPORT extern QWidgetPrivate *qt_widget_private(QWidget *widget); +QPlatformScreen * QPlatformScreen::platformScreenForWidget(const QWidget *widget) +{ + QWidget *window = widget->window(); + QWidgetPrivate *windowPrivate = qt_widget_private(window); + QTLWExtra * topData = windowPrivate->topData(); + QPlatformIntegration *integration = + QApplicationPrivate::platformIntegration(); + return integration->screens()[topData->screenIndex]; +} + /*! \class QPlatformScreen \since 4.8 diff --git a/src/gui/kernel/qplatformscreen_qpa.h b/src/gui/kernel/qplatformscreen_qpa.h index 9080489..1f52764 100644 --- a/src/gui/kernel/qplatformscreen_qpa.h +++ b/src/gui/kernel/qplatformscreen_qpa.h @@ -73,6 +73,10 @@ public: //jl: should setDirty be removed. virtual void setDirty(const QRect &) {} virtual QWidget *topLevelAt(const QPoint &point) const; + + //jl: should this function be in QPlatformIntegration + //jl: maybe screenForWidget is a better name? + static QPlatformScreen *platformScreenForWidget(const QWidget *widget); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformwindow_qpa.cpp b/src/gui/kernel/qplatformwindow_qpa.cpp index 29eaa82..b6b6693 100644 --- a/src/gui/kernel/qplatformwindow_qpa.cpp +++ b/src/gui/kernel/qplatformwindow_qpa.cpp @@ -41,6 +41,7 @@ #include "qplatformwindow_qpa.h" +#include <QtGui/qwindowsysteminterface_qpa.h> #include <QtGui/qwidget.h> class QPlatformWindowPrivate @@ -171,6 +172,24 @@ void QPlatformWindow::setOpacity(qreal level) } /*! + Reimplement to let Qt be able to request activation/focus for a window + + Some window systems will probably not have callbacks for this functionality, + and then calling QWindowSystemInterface::handleWindowActivated(QWidget *w) + would be sufficient. + + If the window system has some event handling/callbacks then call + QWindowSystemInterface::handleWindowActivated(QWidget *w) when the window system + gives the notification. + + Default implementation calls QWindowSystem::handleWindowActivated(QWidget *w) +*/ +void QPlatformWindow::requestActivateWindow() +{ + QWindowSystemInterface::handleWindowActivated(widget()); +} + +/*! Reimplement to return the glContext associated with the window. */ QPlatformGLContext *QPlatformWindow::glContext() const diff --git a/src/gui/kernel/qplatformwindow_qpa.h b/src/gui/kernel/qplatformwindow_qpa.h index 4f6fedc..abc35d1 100644 --- a/src/gui/kernel/qplatformwindow_qpa.h +++ b/src/gui/kernel/qplatformwindow_qpa.h @@ -80,6 +80,7 @@ public: virtual void lower(); virtual void setOpacity(qreal level); + virtual void requestActivateWindow(); virtual QPlatformGLContext *glContext() const; protected: diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index b2d9ca6..ddbf53f 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -89,9 +89,16 @@ #include <private/qcocoaview_mac_p.h> #include <private/qkeymapper_p.h> #include <private/qwidget_p.h> +#include <private/qcocoawindow_mac_p.h> QT_BEGIN_NAMESPACE +#ifdef QT_MAC_USE_COCOA +// Cmd + left mousebutton should produce a right button +// press (mainly for mac users with one-button mice): +static bool qt_leftButtonIsRightButton = false; +#endif + Q_GLOBAL_STATIC(QMacWindowFader, macwindowFader); QMacWindowFader::QMacWindowFader() @@ -141,6 +148,9 @@ void QMacWindowFader::performFade() extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp; extern QWidget * mac_mouse_grabber; extern QWidget *qt_button_down; //qapplication_mac.cpp +extern QPointer<QWidget> qt_last_mouse_receiver; +extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); +extern void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); // qcursor_mac.mm void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) { @@ -167,6 +177,70 @@ void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) } } } +struct dndenum_mapper +{ + NSDragOperation mac_code; + Qt::DropAction qt_code; + bool Qt2Mac; +}; + +#ifdef QT_MAC_USE_COCOA && __OBJC__ + +static dndenum_mapper dnd_enums[] = { + { NSDragOperationLink, Qt::LinkAction, true }, + { NSDragOperationMove, Qt::MoveAction, true }, + { NSDragOperationCopy, Qt::CopyAction, true }, + { NSDragOperationGeneric, Qt::CopyAction, false }, + { NSDragOperationEvery, Qt::ActionMask, false }, + { NSDragOperationNone, Qt::IgnoreAction, false } +}; + +NSDragOperation qt_mac_mapDropAction(Qt::DropAction action) +{ + for (int i=0; dnd_enums[i].qt_code; i++) { + if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) { + return dnd_enums[i].mac_code; + } + } + return NSDragOperationNone; +} + +NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions) +{ + NSDragOperation nsActions = NSDragOperationNone; + for (int i=0; dnd_enums[i].qt_code; i++) { + if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code)) + nsActions |= dnd_enums[i].mac_code; + } + return nsActions; +} + +Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions) +{ + Qt::DropAction action = Qt::IgnoreAction; + for (int i=0; dnd_enums[i].mac_code; i++) { + if (nsActions & dnd_enums[i].mac_code) + return dnd_enums[i].qt_code; + } + return action; +} + +Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions) +{ + Qt::DropActions actions = Qt::IgnoreAction; + for (int i=0; dnd_enums[i].mac_code; i++) { + if (nsActions & dnd_enums[i].mac_code) + actions |= dnd_enums[i].qt_code; + } + return actions; +} + +Q_GLOBAL_STATIC(DnDParams, currentDnDParameters); +DnDParams *macCurrentDnDParameters() +{ + return currentDnDParameters(); +} +#endif bool macWindowIsTextured( void * /*OSWindowRef*/ window ) { @@ -301,7 +375,7 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget) HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(widget->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip); return (nativeSizeGrip != 0); #else - return [[reinterpret_cast<NSView *>(widget->winId()) window] showsResizeIndicator]; + return [[reinterpret_cast<NSView *>(widget->effectiveWinId()) window] showsResizeIndicator]; #endif } struct qt_mac_enum_mapper @@ -517,8 +591,12 @@ static const KeyPair entries[NumEntries] = { { NSNewlineCharacter, Qt::Key_Return }, { NSCarriageReturnCharacter, Qt::Key_Return }, { NSBackTabCharacter, Qt::Key_Backtab }, - { NSDeleteCharacter, Qt::Key_Delete }, { kEscapeCharCode, Qt::Key_Escape }, + // Cocoa sends us delete when pressing backspace! + // (NB when we reverse this list in qtKey2CocoaKey, there + // will be two indices of Qt::Key_Backspace. But is seems to work + // ok for menu shortcuts (which uses that function): + { NSDeleteCharacter, Qt::Key_Backspace }, { NSUpArrowFunctionKey, Qt::Key_Up }, { NSDownArrowFunctionKey, Qt::Key_Down }, { NSLeftArrowFunctionKey, Qt::Key_Left }, @@ -731,7 +809,6 @@ Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum) return Qt::NoButton; } -// Helper to share code between QCocoaWindow and QCocoaView bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent) { #ifndef QT_MAC_USE_COCOA @@ -743,6 +820,7 @@ bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEve EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef])); Q_ASSERT(key_event); unsigned int info = 0; + if ([event type] == NSKeyDown) { NSString *characters = [event characters]; if ([characters length]) { @@ -752,19 +830,12 @@ bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEve } } - // Redirect keys to alien widgets. - if (widgetToGetEvent->testAttribute(Qt::WA_NativeWindow) == false) { - widgetToGetEvent = qApp->focusWidget(); - } - - if (widgetToGetEvent == 0) - return false; - if (qt_mac_sendMacEventToWidget(widgetToGetEvent, key_event)) return true; if (mustUseCocoaKeyEvent()) return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent); + bool consumed = qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &info, true); return consumed && (info != 0); #endif @@ -788,7 +859,6 @@ void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget #endif } - QPointF flipPoint(const NSPoint &p) { return QPointF(p.x, flipYCoordinate(p.y)); @@ -804,21 +874,15 @@ NSPoint flipPoint(const QPointF &p) return NSMakePoint(p.x(), flipYCoordinate(p.y())); } -void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* NSEvent* */mouseEvent, - QWidget *widgetToGetEvent, bool &leftButtonIsRightButton) +#if QT_MAC_USE_COCOA && __OBJC__ + +void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event) { -#ifndef QT_MAC_USE_COCOA - Q_UNUSED(eventWindow); - Q_UNUSED(mouseEvent); - Q_UNUSED(widgetToGetEvent); - Q_UNUSED(leftButtonIsRightButton); -#else + QWidget *widgetToGetEvent = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; if (widgetToGetEvent == 0) return; - NSWindow *window = static_cast<NSWindow *>(eventWindow); - NSEvent *event = static_cast<NSEvent *>(mouseEvent); - NSEventType evtType = [event type]; + NSEventType evtType = [event type]; QPoint qlocalPoint; QPoint qglobalPoint; bool processThisEvent = false; @@ -936,12 +1000,12 @@ void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* : QEvent::MouseButtonDblClick; if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) { button = Qt::RightButton; - leftButtonIsRightButton = true; + qt_leftButtonIsRightButton = true; } } else if (eventType == QEvent::NonClientAreaMouseButtonRelease || eventType == QEvent::MouseButtonRelease) { - if (button == Qt::LeftButton && leftButtonIsRightButton) { + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) { button = Qt::RightButton; - leftButtonIsRightButton = false; + qt_leftButtonIsRightButton = false; } } @@ -961,155 +1025,268 @@ void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* // However we might need to unset it if the event is Release. if (eventType == QEvent::MouseButtonRelease) qt_button_down = 0; -#endif } -bool qt_mac_handleMouseEvent(void * /* NSView * */view, void * /* NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button) +QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent) +{ + if (QWidget *popup = QApplication::activePopupWidget()) { + QWidget *focusInPopup = popup->focusWidget(); + return focusInPopup ? focusInPopup : popup; + } + + QWidget *widgetToGetKey = qApp->focusWidget(); + if (!widgetToGetKey) + widgetToGetKey = widgetThatReceivedEvent; + + return widgetToGetKey; +} + +// This function will find the widget that should receive the +// mouse event. Because of explicit/implicit mouse grabs, popups, +// etc, this might not end up being the same as the widget under +// the mouse (which is more interresting when handling enter/leave +// events +QWidget *qt_mac_getTargetForMouseEvent( + // You can call this function without providing an event. + // If so, set returnGlobalPoint before the call. + NSEvent *event, + QEvent::Type eventType, + QPoint &returnLocalPoint, + QPoint &returnGlobalPoint, + QWidget *nativeWidget, + QWidget **returnWidgetUnderMouse) { -#ifndef QT_MAC_USE_COCOA - Q_UNUSED(view); Q_UNUSED(event); - Q_UNUSED(eventType); - Q_UNUSED(button); - return false; -#else - QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view); - NSEvent *theEvent = static_cast<NSEvent *>(event); + returnGlobalPoint = flipPoint([NSEvent mouseLocation]).toPoint(); + QWidget *mouseGrabber = QWidget::mouseGrabber(); + bool buttonDownNotBlockedByModal = qt_button_down && !QApplicationPrivate::isBlockedByModal(qt_button_down); + + // Resolve the widget under the mouse: + QWidget *widgetUnderMouse = 0; + if (nativeWidget) { + QPoint p = nativeWidget->mapFromGlobal(returnGlobalPoint); + widgetUnderMouse = nativeWidget->childAt(p); + if (!widgetUnderMouse){ + // Cocoa will redirct mouse event to the current + // mouse down widget, which is not what we want for our + // widgetUnderMouse assignment. So we need to check + // if we are actually inside nativeView: + if (nativeWidget->rect().contains(p)) { + widgetUnderMouse = nativeWidget; + } else { + // Ok, fallback to find the widget under mouse ourselves. + widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint); + } + } + } else { + // Calling QApplication::widgetAt is potentially slow, hence the + // reason we avoid it if we can. So supplying a nativeWidget to + // this function is mostly an optimization. But at the same time, + // calling QApplication::widgetAt fails for QMacNativeWidget... + widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint); + } - // Give the Input Manager a chance to process the mouse events. - NSInputManager *currentIManager = [NSInputManager currentInputManager]; - if (currentIManager && [currentIManager wantsToHandleMouseEvents]) { - [currentIManager handleMouseEvent:theEvent]; - } + if (widgetUnderMouse) { + // Check if widgetUnderMouse is blocked by a modal + // window, or the mouse if over the frame strut: + if (widgetUnderMouse == qt_button_down) { + // Small optimization to avoid an extra call to isBlockedByModal: + if (buttonDownNotBlockedByModal == false) + widgetUnderMouse = 0; + } else if (QApplicationPrivate::isBlockedByModal(widgetUnderMouse)) { + widgetUnderMouse = 0; + } - // Handle tablet events (if any) first. - if (qt_mac_handleTabletEvent(theView, theEvent)) { - // Tablet event was handled. In Qt we aren't supposed to send the mouse event. - return true; + if (widgetUnderMouse && widgetUnderMouse->isWindow()) { + // Exclude the titlebar (and frame strut) when finding widget under mouse: + QPoint p = widgetUnderMouse->mapFromGlobal(returnGlobalPoint); + if (!widgetUnderMouse->rect().contains(p)) + widgetUnderMouse = 0; + } + } + if (returnWidgetUnderMouse) + *returnWidgetUnderMouse = widgetUnderMouse; + + // Resolve the target for the mouse event. Default will be widgetUnderMouse, except + // if there is a popup-"grab", mousegrab, or button-down-"grab": + QWidget *popup = QApplication::activePopupWidget(); + if (popup && !mouseGrabber) { + if (!popup->isAncestorOf(widgetUnderMouse)) { + // The popup will always grab the mouse unless the + // mouse is over a child, or the user scrolls: + if (eventType == QEvent::Wheel) + return 0; + returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); + return popup; + } else if (popup == widgetUnderMouse) { + returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); + return popup; + } else { + returnLocalPoint = widgetUnderMouse->mapFromGlobal(returnGlobalPoint); + return widgetUnderMouse; + } } - NSPoint windowPoint = [theEvent locationInWindow]; - NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint]; + QWidget *target = mouseGrabber; + if (!target && buttonDownNotBlockedByModal) + target = qt_button_down; + if (!target) + target = widgetUnderMouse; + if (!target) + return 0; - // Find the widget that *should* get the event (e.g., maybe it was a pop-up, - // they always get the mouse event). - QWidget *qwidget = [theView qt_qwidget]; - QWidget *widgetToGetMouse = 0; - NSView *tmpView = 0; - QWidget *popup = qAppInstance()->activePopupWidget(); - QPoint qglobalPoint(flipPoint(globalPoint).toPoint()); + returnLocalPoint = target->mapFromGlobal(returnGlobalPoint); + return target; +} - if (popup) { - widgetToGetMouse = popup; - tmpView = qt_mac_nativeview_for(popup); - windowPoint = [[tmpView window] convertScreenToBase:globalPoint]; - - QPoint qWindowPoint(windowPoint.x, windowPoint.y); - if (widgetToGetMouse->rect().contains(qWindowPoint)) { - // Keeping the mouse pressed on a combobox button will make - // the popup pop in front of the mouse. But all mouse events - // will be sendt to the button. Since we want mouse events - // to be sendt to widgets inside the popup, we search for the - // widget in front of the mouse: - tmpView = [tmpView hitTest:windowPoint]; - if (!tmpView) - return false; - widgetToGetMouse = - [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(tmpView) qt_qwidget]; - } +static inline void qt_mac_checkEnterLeaveForNativeWidgets(QWidget *maybeEnterWidget) +{ + // Dispatch enter/leave for the cases where QApplicationPrivate::sendMouseEvent do + // not. This will in general be the cases when alien widgets are not involved: + // 1. from a native widget to another native widget or + // 2. from a native widget to no widget + // 3. from no widget to a native or alien widget + + if (qt_button_down || QWidget::mouseGrabber()) + return; + if ((maybeEnterWidget == qt_last_mouse_receiver) && qt_last_mouse_receiver) + return; + + if (maybeEnterWidget) { + if (!qt_last_mouse_receiver) { + // case 3 + QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, 0); + qt_last_mouse_receiver = maybeEnterWidget; + } else if (qt_last_mouse_receiver->internalWinId() && maybeEnterWidget->internalWinId()) { + // case 1 + if (qt_last_mouse_receiver->isVisible()) { + QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, qt_last_mouse_receiver); + qt_last_mouse_receiver = maybeEnterWidget; + } + } // else at lest one of the widgets are alien, so enter/leave will be handled in QApplicationPrivate } else { - extern QWidget * qt_button_down; //qapplication_mac.cpp - QPoint pos; - widgetToGetMouse = QApplicationPrivate::pickMouseReceiver(qwidget, qglobalPoint, - pos, eventType, - button, qt_button_down, 0); - if (widgetToGetMouse) - tmpView = qt_mac_nativeview_for(widgetToGetMouse); + if (qt_last_mouse_receiver && qt_last_mouse_receiver->internalWinId()) { + // case 2 + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + // This seems to be the only case where we need to update qt_last_mouse_receiver + // from the mac specific code. Otherwise, QApplicationPrivate::sendMouseEvent + // will handle it: + qt_last_mouse_receiver = 0; + } } +} + +bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget) +{ + // Give the Input Manager a chance to process the mouse events. + NSInputManager *currentIManager = [NSInputManager currentInputManager]; + if (currentIManager && [currentIManager wantsToHandleMouseEvents]) { + [currentIManager handleMouseEvent:event]; + } + + // Find the widget that should receive the event, and the widget under the mouse. Those + // can differ if an implicit or explicit mouse grab is active: + QWidget *widgetUnderMouse = 0; + QPoint localPoint, globalPoint; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(event, eventType, localPoint, globalPoint, nativeWidget, &widgetUnderMouse); if (!widgetToGetMouse) return false; - NSPoint localPoint = [tmpView convertPoint:windowPoint fromView:nil]; - QPoint qlocalPoint = QPoint(localPoint.x, localPoint.y); + if (!nativeWidget) { + // Path typically taken for mouse moves (send + // directly from [QCocoaWindow sendEvent] + if (!widgetUnderMouse) + return false; + nativeWidget = widgetUnderMouse->internalWinId() ? + widgetUnderMouse : widgetUnderMouse->nativeParentWidget(); + if (!nativeWidget) + return false; + } + NSView *view = qt_mac_effectiveview_for(nativeWidget); - // Search for alien child widgets (either on this qwidget or on the popup) - if (widgetToGetMouse->testAttribute(Qt::WA_NativeWindow) == false || qt_widget_private(widgetToGetMouse)->hasAlienChildren) { - QPoint qScreenPoint = flipPoint(globalPoint).toPoint(); -#ifdef ALIEN_DEBUG - qDebug() << "alien mouse event" << qScreenPoint << possibleAlien; -#endif - QWidget *possibleAlien = widgetToGetMouse->childAt(qlocalPoint); - if (possibleAlien) { - qlocalPoint = possibleAlien->mapFromGlobal(widgetToGetMouse->mapToGlobal(qlocalPoint)); - widgetToGetMouse = possibleAlien; - } + // Handle tablet events (if any) first. + if (qt_mac_handleTabletEvent(view, event)) { + // Tablet event was handled. In Qt we aren't supposed to send the mouse event. + return true; } - EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([theEvent eventRef])); + EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([event eventRef])); if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent)) return true; - // Yay! All the special cases are handled, it really is just a normal mouse event. - Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]); - NSInteger clickCount = [theEvent clickCount]; - Qt::MouseButtons buttons = 0; + // Keep previousButton to make sure we don't send double click + // events when the user double clicks using two different buttons: static Qt::MouseButton previousButton = Qt::NoButton; + + Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]); + NSInteger clickCount = [event clickCount]; + Qt::MouseButtons buttons = 0; { UInt32 mac_buttons; if (GetEventParameter(carbonEvent, kEventParamMouseChord, typeUInt32, 0, sizeof(mac_buttons), 0, &mac_buttons) == noErr) buttons = qt_mac_get_buttons(mac_buttons); } + + // Send enter/leave events for the cases when QApplicationPrivate::sendMouseEvent do not: + qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse); + switch (eventType) { default: qWarning("not handled! %d", eventType); break; case QEvent::MouseMove: + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) + button = Qt::RightButton; break; case QEvent::MouseButtonPress: - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = theView; - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = theEvent; -#ifndef QT_NAMESPACE - Q_ASSERT(clickCount > 0); -#endif + qt_button_down = widgetUnderMouse; if (clickCount % 2 == 0 && (previousButton == Qt::NoButton || previousButton == button)) eventType = QEvent::MouseButtonDblClick; if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) { button = Qt::RightButton; - [theView qt_setLeftButtonIsRightButton: true]; + qt_leftButtonIsRightButton = true; } break; case QEvent::MouseButtonRelease: - if (button == Qt::LeftButton && [theView qt_leftButtonIsRightButton]) { + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) { button = Qt::RightButton; - [theView qt_setLeftButtonIsRightButton: false]; + qt_leftButtonIsRightButton = false; } qt_button_down = 0; break; } - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->localPoint = localPoint; - QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods); -#ifdef ALIEN_DEBUG - qDebug() << "sending mouse event to" << widgetToGetMouse; -#endif - extern QWidget *qt_button_down; - extern QPointer<QWidget> qt_last_mouse_receiver; + qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); - if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false) - qt_sendSpontaneousEvent(widgetToGetMouse, &qme); - else - QApplicationPrivate::sendMouseEvent(widgetToGetMouse, &qme, widgetToGetMouse, qwidget, &qt_button_down, - qt_last_mouse_receiver); + DnDParams *dndParams = currentDnDParameters(); + dndParams->view = view; + dndParams->theEvent = event; + dndParams->globalPoint = globalPoint; + + // Send the mouse event: + QMouseEvent qme(eventType, localPoint, globalPoint, button, buttons, keyMods); + QApplicationPrivate::sendMouseEvent( + widgetToGetMouse, &qme, widgetUnderMouse, nativeWidget, + &qt_button_down, qt_last_mouse_receiver, true); if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) { - QContextMenuEvent qcme(QContextMenuEvent::Mouse, qlocalPoint, qglobalPoint, keyMods); + QContextMenuEvent qcme(QContextMenuEvent::Mouse, localPoint, globalPoint, keyMods); qt_sendSpontaneousEvent(widgetToGetMouse, &qcme); } + + if (eventType == QEvent::MouseButtonRelease) { + // A mouse button was released, which means that the implicit grab was + // released. We therefore need to re-check if should send (delayed) enter leave events: + // qt_button_down has now become NULL since the call at the top of the function. + widgetToGetMouse = qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, nativeWidget, &widgetUnderMouse); + qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse); + } + previousButton = button; return true; -#endif } +#endif bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */tabletEvent) { @@ -1293,17 +1470,17 @@ void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivat } #endif // QT_MAC_USE_COCOA +#if QT_MAC_USE_COCOA void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show) { if(!window) return; -#if QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; OSWindowRef theWindow = static_cast<OSWindowRef>(window); NSToolbar *macToolbar = [theWindow toolbar]; [macToolbar setShowsBaselineSeparator:show]; -#endif // QT_MAC_USE_COCOA } +#endif // QT_MAC_USE_COCOA QStringList qt_mac_NSArrayToQStringList(void *nsarray) { @@ -1323,6 +1500,7 @@ void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list) return result; } +#if QT_MAC_USE_COCOA void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow) { if (!widgetForWindow) @@ -1347,6 +1525,7 @@ void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow) [window setShowsToolbarButton:uint(flags & Qt::MacWindowToolBarButtonHint) != 0]; } +#endif // QT_MAC_USE_COCOA // Carbon: Make sure you call QDEndContext on the context when done with it. CGContextRef qt_mac_graphicsContextFor(QWidget *widget) @@ -1448,6 +1627,7 @@ void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayI } } +#ifdef QT_MAC_USE_COCOA void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) { OSMenuRef menu = static_cast<OSMenuRef>(theMenu); @@ -1481,12 +1661,24 @@ void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) } } -#ifdef QT_MAC_USE_COCOA -void qt_cocoaChangeOverrideCursor(const QCursor &cursor) +class CocoaPostMessageAfterEventLoopExitHelp : public QObject { - QMacCocoaAutoReleasePool pool; - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) set]; -} + id target; + SEL selector; + int argCount; + id arg1; + id arg2; +public: + CocoaPostMessageAfterEventLoopExitHelp(id target, SEL selector, int argCount, id arg1, id arg2) + : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2){ + deleteLater(); + } + + ~CocoaPostMessageAfterEventLoopExitHelp() + { + qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); + } +}; void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2) { @@ -1500,6 +1692,15 @@ void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2 context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper]; [NSApp postEvent:e atStart:NO]; } + +void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount, id arg1, id arg2) +{ + if (QApplicationPrivate::instance()->threadData->eventLoops.size() <= 1) + qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); + else + new CocoaPostMessageAfterEventLoopExitHelp(target, selector, argCount, arg1, arg2); +} + #endif QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool() @@ -1523,6 +1724,12 @@ void qt_mac_post_retranslateAppMenu() #endif } +QWidgetPrivate *QMacScrollOptimization::_target = 0; +bool QMacScrollOptimization::_inWheelEvent = false; +int QMacScrollOptimization::_dx = 0; +int QMacScrollOptimization::_dy = 0; +QRect QMacScrollOptimization::_scrollRect = QRect(0, 0, -1, -1); + #ifdef QT_MAC_USE_COCOA // This method implements the magic for the drawRectSpecial method. // We draw a line at the upper edge of the content view in order to diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index d61c00f..334d9e8 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -103,6 +103,7 @@ #include <qtimer.h> #include <qtooltip.h> #include <private/qeffects_p.h> +#include <private/qwidget_p.h> #include <qtextdocument.h> #include <qdebug.h> #include <qpoint.h> @@ -144,18 +145,14 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget); void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent); #ifdef QT_MAC_USE_COCOA bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); -void qt_cocoaChangeOverrideCursor(const QCursor &cursor); // These methods exists only for supporting unified mode. void macDrawRectOnTop(void * /*OSWindowRef */ window); void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window); void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *widget); -#endif void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse); +#endif bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent); -void qt_mac_dispatchNCMouseMessage(void */* NSWindow* */eventWindow, void */* NSEvent* */mouseEvent, - QWidget *widgetToGetEvent, bool &leftButtonIsRightButton); -bool qt_mac_handleMouseEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button); bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event); inline QApplication *qAppInstance() { return static_cast<QApplication *>(QCoreApplication::instance()); } struct ::TabletProximityRec; @@ -165,6 +162,29 @@ Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations); QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height); void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, QStyle::StandardPixmap standardIcon = QStyle::SP_CustomBase); + +#if QT_MAC_USE_COCOA && __OBJC__ +struct DnDParams +{ + NSView *view; + NSEvent *theEvent; + QPoint globalPoint; + NSDragOperation performedAction; +}; + +DnDParams *macCurrentDnDParameters(); +NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); +NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); +Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions); +Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); + +QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent); +QWidget *qt_mac_getTargetForMouseEvent(NSEvent *event, QEvent::Type eventType, + QPoint &returnLocalPoint, QPoint &returnGlobalPoint, QWidget *nativeWidget, QWidget **returnWidgetUnderMouse); +bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget); +void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event); +#endif + inline int flipYCoordinate(int y) { return QApplication::desktop()->screenGeometry(0).height() - y; @@ -221,10 +241,64 @@ public: } }; void qt_cocoaPostMessage(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); +void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); #endif #endif +class QMacScrollOptimization { + // This class is made to optimize for the case when the user + // scrolls both horizontally and vertically at the same + // time. This will result in two QWheelEvents (one for each + // direction), which will typically result in two calls to + // QWidget::_scroll_sys. Rather than copying pixels twize on + // screen because of this, we add this helper class to try to + // get away with only one blit. + static QWidgetPrivate *_target; + static bool _inWheelEvent; + static int _dx; + static int _dy; + static QRect _scrollRect; + +public: + static void initDelayedScroll() + { + _inWheelEvent = true; + } + + static bool delayScroll(QWidgetPrivate *target, int dx, int dy, const QRect &scrollRect) + { + if (!_inWheelEvent) + return false; + if (_target && _target != target) + return false; + if (_scrollRect.width() != -1 && _scrollRect != scrollRect) + return false; + + _target = target; + _dx += dx; + _dy += dy; + _scrollRect = scrollRect; + return true; + } + + static void performDelayedScroll() + { + if (!_inWheelEvent) + return; + _inWheelEvent = false; + if (!_target) + return; + + _target->scroll_sys(_dx, _dy, _scrollRect); + + _target = 0; + _dx = 0; + _dy = 0; + _scrollRect = QRect(0, 0, -1, -1); + } +}; + void qt_mac_post_retranslateAppMenu(); void qt_mac_display(QWidget *widget); diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index fdb35d5..40697bf 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -141,6 +141,7 @@ public: int supportsPremultipliedAlpha : 1; int avkonComponentsSupportTransparency : 1; int menuBeingConstructed : 1; + int orientationSet : 1; QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type static CEikButtonGroupContainer *cba; @@ -295,6 +296,7 @@ inline QS60Data::QS60Data() supportsPremultipliedAlpha(0), avkonComponentsSupportTransparency(0), menuBeingConstructed(0), + orientationSet(0), s60ApplicationFactory(0) #ifdef Q_OS_SYMBIAN ,s60InstalledTrapHandler(0) diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index f35afb0..475d4da 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -127,6 +127,10 @@ #include "qtabwidget.h" // Needed in inTabWidget() #endif // QT_KEYPAD_NAVIGATION +#ifdef Q_WS_S60 +#include <aknappui.h> +#endif + // widget/widget data creation count //#define QWIDGET_EXTRA_DEBUG //#define ALIEN_DEBUG @@ -144,6 +148,10 @@ Q_GUI_EXPORT void qt_x11_set_global_double_buffer(bool enable) } #endif +#if defined(QT_MAC_USE_COCOA) +bool qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; +#endif + static inline bool qRectIntersects(const QRect &r1, const QRect &r2) { return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right()) && @@ -300,11 +308,8 @@ QWidgetPrivate::QWidgetPrivate(int version) #endif #elif defined(Q_WS_MAC) , needWindowChange(0) - , hasAlienChildren(0) , window_event(0) , qd_hd(0) -#elif defined (Q_WS_QPA) - , screenNumber(0) #endif { if (!qApp) { @@ -324,6 +329,7 @@ QWidgetPrivate::QWidgetPrivate(int version) hasOwnContext = false; isInUnifiedToolbar = false; unifiedSurface = 0; + touchEventsEnabled = false; #endif // QT_MAC_USE_COCOA #ifdef QWIDGET_EXTRA_DEBUG static int count = 0; @@ -1277,7 +1283,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) } #elif defined(Q_WS_QPA) if (desktopWidget) { - int screen = desktopWidget->d_func()->screenNumber; + int screen = desktopWidget->d_func()->topData()->screenIndex; QPlatformIntegration *platform = QApplicationPrivate::platformIntegration(); platform->moveToScreen(q, screen); } @@ -1305,9 +1311,9 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) if (f & Qt::MSWindowsOwnDC) q->setAttribute(Qt::WA_NativeWindow); -#ifdef Q_WS_MAC - q->setAttribute(Qt::WA_NativeWindow); -#endif +//#ifdef Q_WS_MAC +// q->setAttribute(Qt::WA_NativeWindow); +//#endif q->setAttribute(Qt::WA_QuitOnClose); // might be cleared in adjustQuitOnCloseAttribute() adjustQuitOnCloseAttribute(); @@ -1414,10 +1420,6 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) #ifndef Q_WS_QPA if (QWidget *parent = parentWidget()) { -#ifdef Q_WS_MAC - if (testAttribute(Qt::WA_NativeWindow) == false) - parent->d_func()->hasAlienChildren = true; -#endif if (type & Qt::Window) { if (!parent->testAttribute(Qt::WA_WState_Created)) parent->createWinId(); @@ -1729,6 +1731,7 @@ void QWidgetPrivate::createTLExtra() #if defined(Q_WS_QPA) x->platformWindow = 0; x->platformWindowFormat = QPlatformWindowFormat::defaultFormat(); + x->screenIndex = 0; #endif } } @@ -5400,6 +5403,9 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP return; #if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + if (qt_mac_clearDirtyOnWidgetInsideDrawWidget) + dirtyOnWidget = QRegion(); + // We disable the rendering of QToolBar in the backingStore if // it's supposed to be in the unified toolbar on Mac OS X. if (backingStore && isInUnifiedToolbar) @@ -5487,7 +5493,6 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP //paint the background if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground)) && !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) { - QPainter p(q); paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0); } @@ -5669,10 +5674,12 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis QRect boundingRect; bool dirtyBoundingRect = true; const bool exludeOpaqueChildren = (flags & DontDrawOpaqueChildren); + const bool excludeNativeChildren = (flags & DontDrawNativeChildren); do { QWidget *x = qobject_cast<QWidget*>(siblings.at(index)); - if (x && !(exludeOpaqueChildren && x->d_func()->isOpaque) && !x->isHidden() && !x->isWindow()) { + if (x && !(exludeOpaqueChildren && x->d_func()->isOpaque) && !x->isHidden() && !x->isWindow() + && !(excludeNativeChildren && x->internalWinId())) { if (dirtyBoundingRect) { boundingRect = rgn.boundingRect(); dirtyBoundingRect = false; @@ -10076,7 +10083,13 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) #if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_OS_SYMBIAN) if (newParent && parent && !desktopWidget) { - if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) + if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + // On Mac, toolbars inside the unified title bar will never overlap with + // siblings in the content view. So we skip enforce native siblings in that case + && !d->isInUnifiedToolbar && parentWidget() && parentWidget()->isWindow() +#endif // Q_WS_MAC && QT_MAC_USE_COCOA + ) parent->d_func()->enforceNativeChildren(); else if (parent->d_func()->nativeChildrenForced() || parent->testAttribute(Qt::WA_PaintOnScreen)) setAttribute(Qt::WA_NativeWindow); @@ -10723,7 +10736,13 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) ic->setFocusWidget(0); } } - if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget()) + if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget() +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + // On Mac, toolbars inside the unified title bar will never overlap with + // siblings in the content view. So we skip enforce native siblings in that case + && !d->isInUnifiedToolbar && parentWidget()->isWindow() +#endif // Q_WS_MAC && QT_MAC_USE_COCOA + ) parentWidget()->d_func()->enforceNativeChildren(); if (on && !internalWinId() && testAttribute(Qt::WA_WState_Created)) d->createWinId(); @@ -10856,6 +10875,42 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) d->registerTouchWindow(); #endif break; + case Qt::WA_LockPortraitOrientation: + case Qt::WA_LockLandscapeOrientation: + case Qt::WA_AutoOrientation: { + const Qt::WidgetAttribute orientations[3] = { + Qt::WA_LockPortraitOrientation, + Qt::WA_LockLandscapeOrientation, + Qt::WA_AutoOrientation + }; + + if (on) { + // We can only have one of these set at a time + for (int i = 0; i < 3; ++i) { + if (orientations[i] != attribute) + setAttribute_internal(orientations[i], false, data, d); + } + } + +#ifdef Q_WS_S60 + CAknAppUiBase* appUi = static_cast<CAknAppUiBase*>(CEikonEnv::Static()->EikAppUi()); + const CAknAppUiBase::TAppUiOrientation s60orientations[] = { + CAknAppUiBase::EAppUiOrientationPortrait, + CAknAppUiBase::EAppUiOrientationLandscape, + CAknAppUiBase::EAppUiOrientationAutomatic + }; + CAknAppUiBase::TAppUiOrientation s60orientation = CAknAppUiBase::EAppUiOrientationUnspecified; + for (int i = 0; i < 3; ++i) { + if (testAttribute(orientations[i])) { + s60orientation = s60orientations[i]; + break; + } + } + QT_TRAP_THROWING(appUi->SetOrientationL(s60orientation)); + S60->orientationSet = true; +#endif + break; + } default: break; } diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 376999e..3ba12cd 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -155,7 +155,6 @@ static bool qt_mac_raise_process = true; static OSWindowRef qt_root_win = 0; QWidget *mac_mouse_grabber = 0; QWidget *mac_keyboard_grabber = 0; -extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp #ifndef QT_MAC_USE_COCOA #ifdef QT_NAMESPACE @@ -179,13 +178,14 @@ static CFStringRef kObjectQWidget = CFSTR("com.trolltech.qt.widget"); /***************************************************************************** Externals *****************************************************************************/ +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp extern QWidget *qt_mac_modal_blocked(QWidget *); //qapplication_mac.mm extern void qt_event_request_activate(QWidget *); //qapplication_mac.mm extern bool qt_event_remove_activate(); //qapplication_mac.mm extern void qt_mac_event_release(QWidget *w); //qapplication_mac.mm extern void qt_event_request_showsheet(QWidget *); //qapplication_mac.mm extern void qt_event_request_window_change(QWidget *); //qapplication_mac.mm -extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm +extern QPointer<QWidget> qt_last_mouse_receiver; //qapplication_mac.mm extern IconRef qt_mac_create_iconref(const QPixmap &); //qpixmap_mac.cpp extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.mm extern void qt_mac_update_cursor(); //qcursor_mac.mm @@ -193,7 +193,8 @@ extern bool qt_nograb(); extern CGImageRef qt_mac_create_cgimage(const QPixmap &, bool); //qpixmap_mac.cpp extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp - +extern void qt_mac_setMouseGrabCursor(bool set, QCursor *cursor = 0); // qcursor_mac.mm +extern QPointer<QWidget> topLevelAt_cache; // qapplication_mac.mm /***************************************************************************** QWidget utility functions *****************************************************************************/ @@ -217,22 +218,13 @@ static QSize qt_mac_desktopSize() #ifdef QT_MAC_USE_COCOA static NSDrawer *qt_mac_drawer_for(const QWidget *widget) { - // This only goes one level below the content view so start with the window. - // This works fine for straight Qt stuff, but runs into problems if we are - // embedding, but if that's the case, they probably want to be using - // NSDrawer directly. - NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->winId()); + NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->effectiveWinId()); NSArray *windows = [NSApp windows]; for (NSWindow *window in windows) { NSArray *drawers = [window drawers]; for (NSDrawer *drawer in drawers) { if ([drawer contentView] == widgetView) return drawer; - NSArray *views = [[drawer contentView] subviews]; - for (NSView *view in views) { - if (view == widgetView) - return drawer; - } } } return 0; @@ -314,7 +306,7 @@ bool qt_mac_is_macdrawer(const QWidget *w) bool qt_mac_insideKeyWindow(const QWidget *w) { #ifdef QT_MAC_USE_COCOA - return [[reinterpret_cast<NSView *>(w->winId()) window] isKeyWindow]; + return [[reinterpret_cast<NSView *>(w->effectiveWinId()) window] isKeyWindow]; #else Q_UNUSED(w); #endif @@ -421,7 +413,14 @@ inline static void qt_mac_set_fullscreen_mode(bool b) Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w) { - return reinterpret_cast<OSViewRef>(w->data->winid); + return reinterpret_cast<OSViewRef>(w->internalWinId()); +} + +Q_GUI_EXPORT OSViewRef qt_mac_effectiveview_for(const QWidget *w) +{ + // Get the first non-alien (parent) widget for + // w, and return its NSView (if it has one): + return reinterpret_cast<OSViewRef>(w->effectiveWinId()); } Q_GUI_EXPORT OSViewRef qt_mac_get_contentview_for(OSWindowRef w) @@ -479,11 +478,12 @@ bool qt_isGenuineQWidget(const QWidget *window) Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w) { - OSViewRef hiview = qt_mac_nativeview_for(w); - if (hiview){ + if (OSViewRef hiview = qt_mac_effectiveview_for(w)) { OSWindowRef window = qt_mac_window_for(hiview); - if (!window && qt_isGenuineQWidget(hiview)) { - QWidget *myWindow = w->window(); + if (window) + return window; + + if (qt_isGenuineQWidget(hiview)) { // This is a workaround for NSToolbar. When a widget is hidden // by clicking the toolbar button, Cocoa reparents the widgets // to another window (but Qt doesn't know about it). @@ -491,18 +491,22 @@ Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w) // but at this point it's window is nil, but the window it's being brought // into (the Qt one) is for sure created. // This stops the hierarchy moving under our feet. - if (myWindow != w && qt_mac_window_for(qt_mac_nativeview_for(myWindow))) - return qt_mac_window_for(qt_mac_nativeview_for(myWindow)); + QWidget *toplevel = w->window(); + if (toplevel != w) { + hiview = qt_mac_nativeview_for(toplevel); + if (OSWindowRef w = qt_mac_window_for(hiview)) + return w; + } - myWindow->d_func()->createWindow_sys(); - // Reget the hiview since the "create window could potentially move the view (I guess). - hiview = qt_mac_nativeview_for(w); - window = qt_mac_window_for(hiview); + toplevel->d_func()->createWindow_sys(); + // Reget the hiview since "create window" could potentially move the view (I guess). + hiview = qt_mac_nativeview_for(toplevel); + return qt_mac_window_for(hiview); } - return window; } return 0; } + #ifndef QT_MAC_USE_COCOA /* Checks if the current group is a 'stay on top' group. If so, the group gets removed from the hash table */ @@ -580,25 +584,6 @@ inline static void qt_mac_set_window_group_to_popup(OSWindowRef window) } #endif -#ifdef QT_MAC_USE_COCOA -void qt_mac_set_needs_display(QWidget *widget, QRegion region) -{ - NSView *theNSView = qt_mac_nativeview_for(widget); - if (region.isEmpty()) { - [theNSView setNeedsDisplay:YES]; - return; - } - - QVector<QRect> rects = region.rects(); - for (int i = 0; i<rects.count(); ++i) { - const QRect &rect = rects.at(i); - NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); - [theNSView setNeedsDisplayInRect:nsrect]; - } - -} -#endif - inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRect &rect) { if (!widget) @@ -636,6 +621,51 @@ inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const return false; } +void QWidgetPrivate::macSetNeedsDisplay(QRegion region) +{ + Q_Q(QWidget); +#ifndef QT_MAC_USE_COCOA + if (region.isEmpty()) + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); + else if (RgnHandle rgnHandle = region.toQDRgnForUpdate_sys()) + HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); + else + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. +#else + if (NSView *nativeView = qt_mac_nativeview_for(q)) { + // INVARIANT: q is _not_ alien. So we can optimize a little: + if (region.isEmpty()) { + [nativeView setNeedsDisplay:YES]; + } else { + QVector<QRect> rects = region.rects(); + for (int i = 0; i<rects.count(); ++i) { + const QRect &rect = rects.at(i); + NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); + [nativeView setNeedsDisplayInRect:nsrect]; + } + } + } else if (QWidget *effectiveWidget = q->nativeParentWidget()) { + // INVARIANT: q is alien, and effectiveWidget is native. + if (NSView *effectiveView = qt_mac_nativeview_for(effectiveWidget)) { + if (region.isEmpty()) { + const QRect &rect = q->rect(); + QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); + NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); + [effectiveView setNeedsDisplayInRect:nsrect]; + } else { + QVector<QRect> rects = region.rects(); + for (int i = 0; i<rects.count(); ++i) { + const QRect &rect = rects.at(i); + QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); + NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); + [effectiveView setNeedsDisplayInRect:nsrect]; + } + } + } + } +#endif +} + void QWidgetPrivate::macUpdateIsOpaque() { Q_Q(QWidget); @@ -1569,6 +1599,11 @@ OSViewRef qt_mac_create_widget(QWidget *widget, QWidgetPrivate *widgetPrivate, O #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; QT_MANGLE_NAMESPACE(QCocoaView) *view = [[QT_MANGLE_NAMESPACE(QCocoaView) alloc] initWithQWidget:widget widgetPrivate:widgetPrivate]; + +#ifdef ALIEN_DEBUG + qDebug() << "Creating NSView for" << widget; +#endif + if (view && parent) [parent addSubview:view]; return view; @@ -1635,7 +1670,7 @@ bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) // to happen, prevent that here (you really want the thing hidden). if (up >= 0 || topData->resizer != 0) topData->resizer += up; - OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->winId())); + OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->effectiveWinId())); { #ifndef QT_MAC_USE_COCOA WindowClass wclass; @@ -1670,6 +1705,7 @@ bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) void QWidgetPrivate::qt_clean_root_win() { #ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; [qt_root_win release]; #else if(!qt_root_win) @@ -2310,15 +2346,12 @@ void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWin } [windowRef setContentView:nsview]; [nsview setHidden:NO]; - if (q->testAttribute(Qt::WA_DropSiteRegistered)) - registerDropSite(true); transferChildren(); // Tell Cocoa explicit that we wan't the view to receive key events // (regardless of focus policy) because this is how it works on other // platforms (and in the carbon port): - if (!qApp->focusWidget()) - [windowRef makeFirstResponder:nsview]; + [windowRef makeFirstResponder:nsview]; if (topExtra->posFromMove) { updateFrameStrut(); @@ -2349,8 +2382,9 @@ void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWin q->setAttribute(Qt::WA_WState_WindowOpacitySet, false); } - if (qApp->overrideCursor()) - [windowRef disableCursorRects]; + // Its more performant to handle the mouse cursor + // ourselves, expecially when using alien widgets: + [windowRef disableCursorRects]; setWindowLevel(); macUpdateHideOnSuspend(); @@ -2468,8 +2502,6 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO data.crect.setRect(0, 0, desktopSize.width(), desktopSize.height()); dialog = popup = false; // force these flags off } else { - q->setAttribute(Qt::WA_WState_Visible, false); - if (topLevel && (type != Qt::Drawer)) { if (QDesktopWidget *dsk = QApplication::desktop()) { // calc pos/size from screen const bool wasResized = q->testAttribute(Qt::WA_Resized); @@ -2559,6 +2591,8 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO transfer = true; } else if (parentWidget) { // I need to be added to my parent, therefore my parent needs an NSView + // Alien note: a 'window' was supplied as argument, meaning this widget + // is not alien. So therefore the parent cannot be alien either. parentWidget->createWinId(); parent = qt_mac_nativeview_for(parentWidget); } @@ -2630,11 +2664,8 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO data.fstrut_dirty = false; // non-toplevel widgets don't have a frame, so no need to update the strut #ifdef QT_MAC_USE_COCOA - if (q->testAttribute(Qt::WA_NativeWindow) == false || - q->internalWinId() != 0) { -#ifdef ALIEN_DEBUG - qDebug() << "Skipping native widget creation for" << this; -#endif + if (q->testAttribute(Qt::WA_NativeWindow) == false || q->internalWinId() != 0) { + // INVARIANT: q is Alien, and we should not create an NSView to back it up. } else #endif if (OSViewRef osview = qt_mac_create_widget(q, this, qt_mac_nativeview_for(parentWidget))) { @@ -2646,21 +2677,26 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO NSRect bounds = NSMakeRect(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height()); [osview setFrame:bounds]; setWinId((WId)osview); + if (q->isVisible()) { + // If q were Alien before, but now became native (e.g. if a call to + // winId was done from somewhere), we need to show the view immidiatly: + QMacCocoaAutoReleasePool pool; + [osview setHidden:NO]; + } #endif - if (q->testAttribute(Qt::WA_DropSiteRegistered)) - registerDropSite(true); } } updateIsOpaque(); + + if (q->testAttribute(Qt::WA_DropSiteRegistered)) + registerDropSite(true); if (q->hasFocus()) setFocus_sys(); if (!topLevel && initializeWindow) setWSGeometry(); if (destroyid) qt_mac_destructView(destroyid); - if (q->testAttribute(Qt::WA_AcceptTouchEvents)) - registerTouchWindow(); } /*! @@ -2694,16 +2730,29 @@ QWidget::macCGHandle() const return handle(); } +void qt_mac_repaintParentUnderAlienWidget(QWidget *alienWidget) +{ + QWidget *nativeParent = alienWidget->nativeParentWidget(); + if (!nativeParent) + return; + + QPoint globalPos = alienWidget->mapToGlobal(QPoint(0, 0)); + QRect dirtyRect = QRect(nativeParent->mapFromGlobal(globalPos), alienWidget->size()); + nativeParent->repaint(dirtyRect); +} + void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); + QMacCocoaAutoReleasePool pool; d->aboutToDestroy(); if (!isWindow() && parentWidget()) parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); + if (!internalWinId()) + qt_mac_repaintParentUnderAlienWidget(this); d->deactivateWidgetCleanup(); qt_mac_event_release(this); if(testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; setAttribute(Qt::WA_WState_Created, false); QObjectList chldrn = children(); for(int i = 0; i < chldrn.size(); i++) { // destroy all widget children @@ -2759,7 +2808,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) void QWidgetPrivate::transferChildren() { Q_Q(QWidget); - if (!q->testAttribute(Qt::WA_WState_Created)) + if (!q->internalWinId()) return; // Can't add any views anyway QObjectList chlist = q->children(); @@ -2771,7 +2820,7 @@ void QWidgetPrivate::transferChildren() // This seems weird, no need to call it in a loop right? if (!topData()->caption.isEmpty()) setWindowTitle_helper(extra->topextra->caption); - if (w->testAttribute(Qt::WA_WState_Created)) { + if (w->internalWinId()) { #ifndef QT_MAC_USE_COCOA HIViewAddSubview(qt_mac_nativeview_for(q), qt_mac_nativeview_for(w)); #else @@ -2900,11 +2949,11 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) #ifndef QT_MAC_USE_COCOA old_window_event = window_event; #else - OSWindowRef oldWindow = qt_mac_window_for(old_id); if (qt_mac_is_macdrawer(q)) { oldDrawer = qt_mac_drawer_for(q); } if (wasWindow) { + OSWindowRef oldWindow = qt_mac_window_for(old_id); oldToolbar = [oldWindow toolbar]; if (oldToolbar) { [oldToolbar retain]; @@ -3026,7 +3075,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) QPoint QWidget::mapToGlobal(const QPoint &pos) const { Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + if (!internalWinId()) { QPoint p = pos + data->crect.topLeft(); return isWindow() ? p : parentWidget()->mapToGlobal(p); } @@ -3053,7 +3102,7 @@ QPoint QWidget::mapToGlobal(const QPoint &pos) const QPoint QWidget::mapFromGlobal(const QPoint &pos) const { Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + if (!internalWinId()) { QPoint p = isWindow() ? pos : parentWidget()->mapFromGlobal(pos); return p - data->crect.topLeft(); } @@ -3080,28 +3129,12 @@ void QWidgetPrivate::updateSystemBackground() void QWidgetPrivate::setCursor_sys(const QCursor &) { -#ifndef QT_MAC_USE_COCOA qt_mac_update_cursor(); -#else - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; - [qt_mac_window_for(q) invalidateCursorRectsForView:qt_mac_nativeview_for(q)]; - } -#endif } void QWidgetPrivate::unsetCursor_sys() { -#ifndef QT_MAC_USE_COCOA qt_mac_update_cursor(); -#else - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; - [qt_mac_window_for(q) invalidateCursorRectsForView:qt_mac_nativeview_for(q)]; - } -#endif } void QWidgetPrivate::setWindowTitle_sys(const QString &caption) @@ -3243,24 +3276,28 @@ void QWidget::grabMouse() if(mac_mouse_grabber) mac_mouse_grabber->releaseMouse(); mac_mouse_grabber=this; + qt_mac_setMouseGrabCursor(true); } } #ifndef QT_NO_CURSOR -void QWidget::grabMouse(const QCursor &) +void QWidget::grabMouse(const QCursor &cursor) { if(isVisible() && !qt_nograb()) { if(mac_mouse_grabber) mac_mouse_grabber->releaseMouse(); mac_mouse_grabber=this; + qt_mac_setMouseGrabCursor(true, const_cast<QCursor *>(&cursor)); } } #endif void QWidget::releaseMouse() { - if(!qt_nograb() && mac_mouse_grabber == this) + if(!qt_nograb() && mac_mouse_grabber == this) { mac_mouse_grabber = 0; + qt_mac_setMouseGrabCursor(false); + } } void QWidget::grabKeyboard() @@ -3345,35 +3382,10 @@ QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() void QWidgetPrivate::update_sys(const QRect &r) { Q_Q(QWidget); - if (r == q->rect()) { - if (updateRedirectedToGraphicsProxyWidget(q, r)) - return; - dirtyOnWidget += r; -#ifndef QT_MAC_USE_COCOA - HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); -#else - qt_mac_set_needs_display(q, QRegion()); -#endif + if (updateRedirectedToGraphicsProxyWidget(q, r)) return; - } - - int x = r.x(), y = r.y(), w = r.width(), h = r.height(); - if (w < 0) - w = q->data->crect.width() - x; - if (h < 0) - h = q->data->crect.height() - y; - if (w && h) { - const QRect updateRect = QRect(x, y, w, h); - if (updateRedirectedToGraphicsProxyWidget(q, updateRect)) - return; -#ifndef QT_MAC_USE_COCOA - dirtyOnWidget += updateRect; - HIRect r = CGRectMake(x, y, w, h); - HIViewSetNeedsDisplayInRect(qt_mac_nativeview_for(q), &r, true); -#else - [qt_mac_nativeview_for(q) setNeedsDisplayInRect:NSMakeRect(x, y, w, h)]; -#endif - } + dirtyOnWidget += r; + macSetNeedsDisplay(r != q->rect() ? r : QRegion()); } void QWidgetPrivate::update_sys(const QRegion &rgn) @@ -3382,33 +3394,7 @@ void QWidgetPrivate::update_sys(const QRegion &rgn) if (updateRedirectedToGraphicsProxyWidget(q, rgn)) return; dirtyOnWidget += rgn; -#ifndef QT_MAC_USE_COCOA - RgnHandle rgnHandle = rgn.toQDRgnForUpdate_sys(); - if (rgnHandle) - HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); - else { - HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. - } -#else - // Alien support: get the first native ancestor widget (will be q itself in the non-alien case), - // map the coordinates from q space to NSView space and invalidate the rect. - QWidget *nativeParent = q->internalWinId() ? q : q->nativeParentWidget(); - if (nativeParent == 0) - return; - - QVector<QRect> rects = rgn.rects(); - for (int i = 0; i < rects.count(); ++i) { - const QRect &rect = rects.at(i); - - const QRect nativeBoundingRect = QRect( - QPoint(q->mapTo(nativeParent, rect.topLeft())), - QSize(rect.size())); - - [qt_mac_nativeview_for(nativeParent) setNeedsDisplayInRect:NSMakeRect(nativeBoundingRect.x(), - nativeBoundingRect.y(), nativeBoundingRect.width(), - nativeBoundingRect.height())]; - } -#endif + macSetNeedsDisplay(rgn); } bool QWidgetPrivate::isRealWindow() const @@ -3447,7 +3433,6 @@ void QWidgetPrivate::show_sys() data.fstrut_dirty = true; if (realWindow) { - // Delegates can change window state, so record some things earlier. bool isCurrentlyMinimized = (q->windowState() & Qt::WindowMinimized); setModal_sys(); OSWindowRef window = qt_mac_window_for(q); @@ -3494,9 +3479,11 @@ void QWidgetPrivate::show_sys() } } setSubWindowStacking(true); + qt_mac_update_cursor(); #endif if (q->windowType() == Qt::Popup) { - if (q->focusWidget()) + qt_button_down = 0; + if (q->focusWidget()) q->focusWidget()->d_func()->setFocus_sys(); else setFocus_sys(); @@ -3518,21 +3505,32 @@ void QWidgetPrivate::show_sys() #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; - + if (NSView *view = qt_mac_nativeview_for(q)) { + // INVARIANT: q is native. Just show the view: + [view setHidden:NO]; + } else { + // INVARIANT: q is alien. Repaint q instead: + q->repaint(); + } #endif } - if (!QWidget::mouseGrabber()){ - QWidget *enterWidget = QApplication::widgetAt(QCursor::pos()); - QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover); - qt_mouseover = enterWidget; +#ifdef QT_MAC_USE_COCOA + if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_mouse_receiver); + qt_last_mouse_receiver = widgetUnderMouse; } +#endif + topLevelAt_cache = 0; qt_event_request_window_change(q); } - QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt) { #ifndef QT_MAC_USE_COCOA @@ -3613,6 +3611,7 @@ void QWidgetPrivate::hide_sys() } #endif toggleDrawers(false); + qt_mac_update_cursor(); #ifndef QT_MAC_USE_COCOA // Clear modality (because it seems something that we've always done). if (data.window_modality != Qt::NonModal) { @@ -3660,18 +3659,29 @@ void QWidgetPrivate::hide_sys() #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), false); #else - [qt_mac_nativeview_for(q) setHidden:YES]; + if (NSView *view = qt_mac_nativeview_for(q)) { + // INVARIANT: q is native. Just hide the view: + [view setHidden:YES]; + } else { + // INVARIANT: q is alien. Repaint where q is placed instead: + qt_mac_repaintParentUnderAlienWidget(q); + } #endif } - if (!QWidget::mouseGrabber()){ - QWidget *enterWidget = QApplication::widgetAt(QCursor::pos()); - if (enterWidget && enterWidget->data->in_destructor) - enterWidget = 0; - QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover); - qt_mouseover = enterWidget; - } - +#ifdef QT_MAC_USE_COCOA + if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Leave, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_mouse_receiver); + qt_last_mouse_receiver = widgetUnderMouse; + } +#endif + + topLevelAt_cache = 0; qt_event_request_window_change(q); deactivateWidgetCleanup(); qt_mac_event_release(q); @@ -3894,12 +3904,14 @@ void QWidgetPrivate::raise_sys() QWidget *parentWidget = q->parentWidget(); if(parentWidget) { OSWindowRef parentWindow = qt_mac_window_for(parentWidget); - if(parentWindow && [parentWindow isOnActiveSpace]) { - // The window was created in a different space. Therefore if we want - // to show it in the current space we need to recreate it in the new - // space. - recreateMacWindow(); - window = qt_mac_window_for(q); + if(parentWindow && [parentWindow respondsToSelector:@selector(isOnActiveSpace)]) { + if ([parentWindow performSelector:@selector(isOnActiveSpace)]) { + // The window was created in a different space. Therefore if we want + // to show it in the current space we need to recreate it in the new + // space. + recreateMacWindow(); + window = qt_mac_window_for(q); + } } } } @@ -3916,6 +3928,7 @@ void QWidgetPrivate::raise_sys() NSView *parentView = [view superview]; [parentView sortSubviewsUsingFunction:compareViews2Raise context:reinterpret_cast<void *>(view)]; } + topLevelAt_cache = 0; #else if(q->isWindow()) { //raise this window @@ -3956,6 +3969,7 @@ void QWidgetPrivate::lower_sys() NSView *parentView = [view superview]; [parentView sortSubviewsUsingFunction:compareViews2Lower context:reinterpret_cast<void *>(view)]; } + topLevelAt_cache = 0; #else if(q->isWindow()) { SendBehind(qt_mac_window_for(q), 0); @@ -4012,6 +4026,7 @@ void QWidgetPrivate::stackUnder_sys(QWidget *w) #endif } +#ifndef QT_MAC_USE_COCOA /* Modifies the bounds for a widgets backing HIView during moves and resizes. Also updates the widget, either by scrolling its contents or repainting, depending on the WA_StaticContents @@ -4019,7 +4034,6 @@ void QWidgetPrivate::stackUnder_sys(QWidget *w) */ static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRect) { -#ifndef QT_MAC_USE_COCOA HIRect bounds = CGRectMake(newRect.x(), newRect.y(), newRect.width(), newRect.height()); @@ -4111,13 +4125,8 @@ static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRe HIViewSetNeedsDisplayInRect(view, &verticalSlice, true); const HIRect horizontalSlice = CGRectMake(0, starty, startx, stopy); HIViewSetNeedsDisplayInRect(view, &horizontalSlice, true); -#else - Q_UNUSED(oldRect); - NSRect bounds = NSMakeRect(newRect.x(), newRect.y(), - newRect.width(), newRect.height()); - [qt_mac_nativeview_for(q) setFrame:bounds]; -#endif } +#endif /* Helper function for non-toplevel widgets. Helps to map Qt's 32bit @@ -4138,7 +4147,15 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) { Q_Q(QWidget); Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - Q_UNUSED(oldRect); + + if (!q->internalWinId() && QApplicationPrivate::graphicsSystem() != 0) { + // We have no view to move, and no paint engine that + // we can update dirty regions on. So just return: + return; + } + + QMacCocoaAutoReleasePool pool; + /* There are up to four different coordinate systems here: Qt coordinate system for this widget. @@ -4146,13 +4163,31 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) Qt coordinate system for parent X coordinate system for parent (relative to parent's wrect). */ + + // wrect is the same as crect, except that it is + // clipped to fit inside parent (and screen): QRect wrect; - //xrect is the X geometry of my X widget. (starts out in parent's Qt coord sys, and ends up in parent's X coord sys) - QRect xrect = data.crect; + // wrectInParentCoordSys will be the same as wrect, except that it is + // originated in q's parent rather than q itself. It starts out in + // parent's Qt coord system, and ends up in parent's coordinate system: + QRect wrectInParentCoordSys = data.crect; + + // If q's parent has been clipped, parentWRect will + // be filled with the parents clipped crect: QRect parentWRect; + + // Embedded have different meaning on each platform, and on + // Mac, it means that q is a QMacNativeWidget. bool isEmbeddedWindow = (q->isWindow() && topData()->embedded); - if (isEmbeddedWindow) { +#ifdef QT_MAC_USE_COCOA + NSView *nsview = qt_mac_nativeview_for(q); +#endif + if (!isEmbeddedWindow) { + parentWRect = q->parentWidget()->data->wrect; + } else { + // INVARIANT: q's parent view is not owned by Qt. So we need to + // do some extra calls to get the clipped rect of the parent view: #ifndef QT_MAC_USE_COCOA HIViewRef parentView = HIViewGetSuperview(qt_mac_nativeview_for(q)); #else @@ -4171,43 +4206,57 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) const QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX); parentWRect = wrectRange; } - } else { - parentWRect = q->parentWidget()->data->wrect; } if (parentWRect.isValid()) { - // parent is clipped, and we have to clip to the same limit as parent - if (!parentWRect.contains(xrect) && !isEmbeddedWindow) { - xrect &= parentWRect; - wrect = xrect; - //translate from parent's to my Qt coord sys + // INVARIANT: q's parent has been clipped. + // So we fit our own wrects inside it: + if (!parentWRect.contains(wrectInParentCoordSys) && !isEmbeddedWindow) { + wrectInParentCoordSys &= parentWRect; + wrect = wrectInParentCoordSys; + // Make sure wrect is originated in q's coordinate system: wrect.translate(-data.crect.topLeft()); } - //translate from parent's Qt coords to parent's X coords - xrect.translate(-parentWRect.topLeft()); - + // // Make sure wrectInParentCoordSys originated in q's parent coordinate system: + wrectInParentCoordSys.translate(-parentWRect.topLeft()); } else { - // parent is not clipped, we may or may not have to clip + // INVARIANT: we dont know yet the clipping rect of q's parent. + // So we may or may not have to adjust our wrects: if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) { - // This is where the main optimization is: we are already - // clipped, and if our clip is still valid, we can just - // move our window, and do not need to move or clip - // children + // This is where the main optimization is: we have an old wrect from an earlier + // setGeometry call, and the new crect is smaller than it. If the final wrect is + // also inside the old wrect, we can just move q and its children to the new + // location without any clipping: + + // vrect will be the part of q that's will be visible inside + // q's parent. If it inside the old wrect, then we can just move: + QRect vrect = wrectInParentCoordSys & q->parentWidget()->rect(); + vrect.translate(-data.crect.topLeft()); - QRect vrect = xrect & q->parentWidget()->rect(); - vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords if (data.wrect.contains(vrect)) { - xrect = data.wrect; - xrect.translate(data.crect.topLeft()); + wrectInParentCoordSys = data.wrect; + wrectInParentCoordSys.translate(data.crect.topLeft()); #ifndef QT_MAC_USE_COCOA - HIRect bounds = CGRectMake(xrect.x(), xrect.y(), - xrect.width(), xrect.height()); + HIRect bounds = CGRectMake(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); HIViewSetFrame(qt_mac_nativeview_for(q), &bounds); #else - NSRect bounds = NSMakeRect(xrect.x(), xrect.y(), - xrect.width(), xrect.height()); - [qt_mac_nativeview_for(q) setFrame:bounds]; + if (nsview) { + // INVARIANT: q is native. Set view frame: + NSRect bounds = NSMakeRect(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + [nsview setFrame:bounds]; + } else { + // INVARIANT: q is alien. Repaint wrect instead (includes old and new wrect): + QWidget *parent = q->parentWidget(); + QPoint globalPosWRect = parent->mapToGlobal(data.wrect.topLeft()); + + QWidget *nativeParent = q->nativeParentWidget(); + QRect dirtyWRect = QRect(nativeParent->mapFromGlobal(globalPosWRect), data.wrect.size()); + + nativeParent->update(dirtyWRect); + } #endif if (q->testAttribute(Qt::WA_OutsideWSRange)) { q->setAttribute(Qt::WA_OutsideWSRange, false); @@ -4216,7 +4265,8 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; + // If q is Alien, the following call does nothing: + [nsview setHidden:NO]; #endif } } @@ -4226,8 +4276,8 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) #ifndef QT_MAC_USE_COCOA const QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX); - if (!validRange.contains(xrect)) { - // we are too big, and must clip + if (!validRange.contains(wrectInParentCoordSys)) { + // We're too big, and must clip: QPoint screenOffset(0, 0); // offset of the part being on screen const QWidget *parentWidget = q->parentWidget(); while (parentWidget && !parentWidget->isWindow()) { @@ -4239,15 +4289,15 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) 2*WRECT_MAX, 2*WRECT_MAX); - xrect &=cropRect; - wrect = xrect; - wrect.translate(-data.crect.topLeft()); // translate wrect in my Qt coordinates + wrectInParentCoordSys &=cropRect; + wrect = wrectInParentCoordSys; + wrect.translate(-data.crect.topLeft()); } #endif //QT_MAC_USE_COCOA } // unmap if we are outside the valid window system coord system - bool outsideRange = !xrect.isValid(); + bool outsideRange = !wrectInParentCoordSys.isValid(); bool mapWindow = false; if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) { q->setAttribute(Qt::WA_OutsideWSRange, outsideRange); @@ -4255,7 +4305,8 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), false); #else - [qt_mac_nativeview_for(q) setHidden:YES]; + // If q is Alien, the following call does nothing: + [nsview setHidden:YES]; #endif q->setAttribute(Qt::WA_Mapped, false); } else if (!q->isHidden()) { @@ -4266,10 +4317,10 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) if (outsideRange) return; + // Store the new clipped rect: bool jump = (data.wrect != wrect); data.wrect = wrect; - // and now recursively for all children... // ### can be optimized for (int i = 0; i < children.size(); ++i) { @@ -4281,17 +4332,56 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) } } - qt_mac_update_widget_position(q, oldRect, xrect); - - if (jump) +#ifndef QT_MAC_USE_COCOA + // Move the actual HIView: + qt_mac_update_widget_position(q, oldRect, wrectInParentCoordSys); + if (jump) q->update(); +#else + if (nsview) { + // INVARIANT: q is native. Move the actual NSView: + NSRect bounds = NSMakeRect( + wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + [nsview setFrame:bounds]; + if (jump) + q->update(); + } else if (QApplicationPrivate::graphicsSystem() == 0){ + // INVARIANT: q is alien and we use native paint engine. + // Schedule updates where q is moved from and to: + const QWidget *parent = q->parentWidget(); + const QPoint globalPosOldWRect = parent->mapToGlobal(oldRect.topLeft()); + const QPoint globalPosNewWRect = parent->mapToGlobal(wrectInParentCoordSys.topLeft()); + + QWidget *nativeParent = q->nativeParentWidget(); + const QRegion dirtyOldWRect = QRect(nativeParent->mapFromGlobal(globalPosOldWRect), oldRect.size()); + const QRegion dirtyNewWRect = QRect(nativeParent->mapFromGlobal(globalPosNewWRect), wrectInParentCoordSys.size()); + + const bool sizeUnchanged = oldRect.size() == wrectInParentCoordSys.size(); + const bool posUnchanged = oldRect.topLeft() == wrectInParentCoordSys.topLeft(); + + // Resolve/minimize the region that needs to update: + if (sizeUnchanged && q->testAttribute(Qt::WA_OpaquePaintEvent)) { + // INVARIANT: q is opaque, and is only moved (not resized). So in theory we only + // need to blit pixels, and skip a repaint. But we can only make this work if we + // had access to the backbuffer, so we need to update all: + nativeParent->update(dirtyOldWRect | dirtyNewWRect); + } else if (posUnchanged && q->testAttribute(Qt::WA_StaticContents)) { + // We only need to redraw exposed areas: + nativeParent->update(dirtyNewWRect - dirtyOldWRect); + } else { + nativeParent->update(dirtyOldWRect | dirtyNewWRect); + } + } +#endif if (mapWindow && !dontShow) { q->setAttribute(Qt::WA_Mapped); #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; + // If q is Alien, the following call does nothing: + [nsview setHidden:NO]; #endif } } @@ -4353,7 +4443,6 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) QMacCocoaAutoReleasePool pool; bool realWindow = isRealWindow(); - BOOL needDisplay = realWindow ? YES : NO; if (realWindow && !q->testAttribute(Qt::WA_DontShowOnScreen)){ adjustWithinMaxAndMinSize(w, h); @@ -4401,7 +4490,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) if (currTopLeft.x() == x && currTopLeft.y() == y && cocoaFrameRect.size.width != 0 && cocoaFrameRect.size.height != 0) { - [window setFrame:cocoaFrameRect display:needDisplay]; + [window setFrame:cocoaFrameRect display:realWindow]; } else { // The window is moved and resized (or resized to zero). // Since Cocoa usually only sends us a resize callback after @@ -4410,7 +4499,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) // would have the same origin as the setFrame call) we shift the // window back and forth inbetween. cocoaFrameRect.origin.y += 1; - [window setFrame:cocoaFrameRect display:needDisplay]; + [window setFrame:cocoaFrameRect display:realWindow]; cocoaFrameRect.origin.y -= 1; [window setFrameOrigin:cocoaFrameRect.origin]; } @@ -4418,6 +4507,8 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) } else { setGeometry_sys_helper(x, y, w, h, isMove); } + + topLevelAt_cache = 0; } void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isMove) @@ -4468,17 +4559,11 @@ void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isM const QRect oldRect(oldp, olds); if (!isResize && QApplicationPrivate::graphicsSystem()) moveRect(oldRect, x - oldp.x(), y - oldp.y()); + setWSGeometry(false, oldRect); - if (isResize && QApplicationPrivate::graphicsSystem()) { - invalidateBuffer(q->rect()); - if (extra && !graphicsEffect && !extra->mask.isEmpty()) { - QRegion oldRegion(extra->mask.translated(oldp)); - oldRegion &= oldRect; - q->parentWidget()->d_func()->invalidateBuffer(oldRegion); - } else { - q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(oldRect)); - } - } + + if (isResize && QApplicationPrivate::graphicsSystem()) + invalidateBuffer_resizeHelper(oldp, olds); } if(isMove || isResize) { @@ -4558,6 +4643,7 @@ void QWidgetPrivate::updateMaximizeButton_sys() void QWidgetPrivate::scroll_sys(int dx, int dy) { if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) { + // INVARIANT: Alien paint engine scrollChildren(dx, dy); scrollRect(q_func()->rect(), dx, dy); } else { @@ -4565,37 +4651,81 @@ void QWidgetPrivate::scroll_sys(int dx, int dy) } } -void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) +void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &qscrollRect) { - Q_Q(QWidget); + if (QMacScrollOptimization::delayScroll(this, dx, dy, qscrollRect)) + return; + Q_Q(QWidget); if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) { - scrollRect(r, dx, dy); + // INVARIANT: Alien paint engine + scrollRect(qscrollRect, dx, dy); return; } - const bool valid_rect = r.isValid(); - if (!q->updatesEnabled() && (valid_rect || q->children().isEmpty())) - return; + static int accelEnv = -1; + if (accelEnv == -1) { + accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0; + } - qt_event_request_window_change(q); + // Scroll the whole widget if qscrollRect is not valid: + QRect validScrollRect = qscrollRect.isValid() ? qscrollRect : q->rect(); + validScrollRect &= clipRect(); + + // If q is overlapped by other widgets, we cannot just blit pixels since + // this will move overlapping widgets as well. In case we just update: + const bool overlapped = isOverlapped(validScrollRect.translated(data.crect.topLeft())); + const bool accelerateScroll = accelEnv && isOpaque && !overlapped; + const bool isAlien = (q->internalWinId() == 0); + const QPoint scrollDelta(dx, dy); + + // If qscrollRect is valid, we are _not_ supposed to scroll q's children (as documented). + // But we do scroll children (and the whole of q) if qscrollRect is invalid. This case is + // documented as undefined, but we exploit it to help factor our code into one function. + const bool scrollChildren = !qscrollRect.isValid(); + + if (!q->updatesEnabled()) { + // We are told not to update anything on q at this point. So unless + // we are supposed to scroll children, we bail out early: + if (!scrollChildren || q->children().isEmpty()) + return; + } + + if (!accelerateScroll) { + if (overlapped) { + QRegion region(validScrollRect); + subtractOpaqueSiblings(region); + update_sys(region); + }else { + update_sys(qscrollRect); + } + return; + } #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; +#else + Q_UNUSED(isAlien); + // We're not sure what the following call is supposed to achive + // but until we see what it breaks, we don't bring it into the + // Cocoa port: + qt_event_request_window_change(q); #endif - if(!valid_rect) { // scroll children - QPoint pd(dx, dy); - QWidgetList moved; - QObjectList chldrn = q->children(); - for(int i = 0; i < chldrn.size(); i++) { //first move all children - QObject *obj = chldrn.at(i); - if(obj->isWidgetType()) { - QWidget *w = (QWidget*)obj; - if(!w->isWindow()) { - w->data->crect = QRect(w->pos() + pd, w->size()); - if (w->testAttribute(Qt::WA_WState_Created)) { + // First move all native children. Alien children will indirectly be + // moved when the parent is scrolled. All directly or indirectly moved + // children will receive a move event before the function call returns. + QWidgetList movedChildren; + if (scrollChildren) { + QObjectList children = q->children(); + + for (int i=0; i<children.size(); i++) { + QObject *obj = children.at(i); + if (QWidget *w = qobject_cast<QWidget*>(obj)) { + if (!w->isWindow()) { + w->data->crect = QRect(w->pos() + scrollDelta, w->size()); #ifndef QT_MAC_USE_COCOA + if (w->testAttribute(Qt::WA_WState_Created)) { HIRect bounds = CGRectMake(w->data->crect.x(), w->data->crect.y(), w->data->crect.width(), w->data->crect.height()); HIViewRef hiview = qt_mac_nativeview_for(w); @@ -4606,83 +4736,118 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) HIViewSetFrame(hiview, &bounds); if (opaque) HIViewSetDrawingEnabled(hiview, true); + } #else - [qt_mac_nativeview_for(w) - setFrame:NSMakeRect(w->data->crect.x(), w->data->crect.y(), - w->data->crect.width(), w->data->crect.height())]; -#endif + if (NSView *view = qt_mac_nativeview_for(w)) { + // INVARIANT: w is not alien + [view setFrame:NSMakeRect( + w->data->crect.x(), w->data->crect.y(), + w->data->crect.width(), w->data->crect.height())]; } - moved.append(w); +#endif + movedChildren.append(w); } } } - //now send move events (do not do this in the above loop, breaks QAquaFocusWidget) - for(int i = 0; i < moved.size(); i++) { - QWidget *w = moved.at(i); - QMoveEvent e(w->pos(), w->pos() - pd); - QApplication::sendEvent(w, &e); - } } - if (!q->testAttribute(Qt::WA_WState_Created) || !q->isVisible()) - return; + if (q->testAttribute(Qt::WA_WState_Created) && q->isVisible()) { + // Scroll q itself according to the qscrollRect, and + // call update on any exposed areas so that they get redrawn: - OSViewRef view = qt_mac_nativeview_for(q); #ifndef QT_MAC_USE_COCOA - HIRect scrollrect = CGRectMake(r.x(), r.y(), r.width(), r.height()); - OSStatus err = _HIViewScrollRectWithOptions(view, valid_rect ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); - if (err) { - // The only parameter that can go wrong, is the rect. - qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect"); - scrollrect = CGRectMake(qMax(r.x(), 0), qMax(r.y(), 0), - qMin(r.width(), q->width()), qMin(r.height(), q->height())); - _HIViewScrollRectWithOptions(view, valid_rect ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); - } + OSViewRef view = qt_mac_nativeview_for(q); + HIRect scrollrect = CGRectMake(qscrollRect.x(), qscrollRect.y(), qscrollRect.width(), qscrollRect.height()); + OSStatus err = _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); + if (err) { + // The only parameter that can go wrong, is the rect. + qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect"); + scrollrect = CGRectMake(qMax(qscrollRect.x(), 0), qMax(qscrollRect.y(), 0), + qMin(qscrollRect.width(), q->width()), qMin(qscrollRect.height(), q->height())); + _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); + } #else - NSRect scrollRect = valid_rect ? NSMakeRect(r.x(), r.y(), r.width(), r.height()) - : NSMakeRect(0, 0, q->width(), q->height()); + QWidget *nativeWidget = isAlien ? q->nativeParentWidget() : q; + if (!nativeWidget) + return; + OSViewRef view = qt_mac_nativeview_for(nativeWidget); + if (!view) + return; - // calc the updateRect - NSRect deltaXRect = { {0, 0}, {0, 0} }; - NSRect deltaYRect = { {0, 0}, {0, 0} }; - if (dy != 0) { - deltaYRect.size.width = scrollRect.size.width; - if (dy > 0) { - deltaYRect.size.height = dy; - } else { - deltaYRect.size.height = -dy; - deltaYRect.origin.y = scrollRect.size.height + dy; + // Calculate the rectangles that needs to be redrawn + // after the scroll. This will be source rect minus destination rect: + QRect deltaXRect; + if (dx != 0) { + deltaXRect.setY(validScrollRect.y()); + deltaXRect.setHeight(validScrollRect.height()); + if (dx > 0) { + deltaXRect.setX(validScrollRect.x()); + deltaXRect.setWidth(dx); + } else { + deltaXRect.setX(validScrollRect.x() + validScrollRect.width() + dx); + deltaXRect.setWidth(-dx); + } } - } - if (dx != 0) { - deltaXRect.size.height = scrollRect.size.height; - if (dx > 0) { - deltaXRect.size.width = dx; - } else { - deltaXRect.size.width = -dx; - deltaXRect.origin.x = scrollRect.size.width + dx; + + QRect deltaYRect; + if (dy != 0) { + deltaYRect.setX(validScrollRect.x()); + deltaYRect.setWidth(validScrollRect.width()); + if (dy > 0) { + deltaYRect.setY(validScrollRect.y()); + deltaYRect.setHeight(dy); + } else { + deltaYRect.setY(validScrollRect.y() + validScrollRect.height() + dy); + deltaYRect.setHeight(-dy); + } } - } - // ### Scroll the dirty regions as well, the following is not correct. - QRegion displayRegion = r.isNull() ? dirtyOnWidget : (dirtyOnWidget & r); - const QVector<QRect> &rects = dirtyOnWidget.rects(); - const QVector<QRect>::const_iterator end = rects.end(); - QVector<QRect>::const_iterator it = rects.begin(); - while (it != end) { - const QRect rect = *it; - const NSRect dirtyRect = NSMakeRect(rect.x() + dx, rect.y() + dy, - rect.width(), rect.height()); - [view setNeedsDisplayInRect:dirtyRect]; - ++it; - } + if (isAlien) { + // Adjust the scroll rect to the location as seen from the native parent: + QPoint scrollTopLeftInsideNative = nativeWidget->mapFromGlobal(q->mapToGlobal(validScrollRect.topLeft())); + validScrollRect.moveTo(scrollTopLeftInsideNative); + } + + // Make the pixel copy rect within the validScrollRect bounds: + NSRect nsscrollRect = NSMakeRect( + validScrollRect.x() + (dx < 0 ? -dx : 0), + validScrollRect.y() + (dy < 0 ? -dy : 0), + validScrollRect.width() + (dx > 0 ? -dx : 0), + validScrollRect.height() + (dy > 0 ? -dy : 0)); + + NSSize deltaSize = NSMakeSize(dx, dy); + [view scrollRect:nsscrollRect by:deltaSize]; + + // Some areas inside the scroll rect might have been marked as dirty from before, which + // means that they are scheduled to be redrawn. But as we now scroll, those dirty rects + // should also move along to ensure that q receives repaints on the correct places. + // Since some of the dirty rects might lay outside, or only intersect with, the scroll + // rect, the old calls to setNeedsDisplay still makes sense. + // NB: Using [view translateRectsNeedingDisplayInRect:nsscrollRect by:deltaSize] have + // so far not been proven fruitful to solve this problem. + const QVector<QRect> &dirtyRectsToScroll = dirtyOnWidget.rects(); + for (int i=0; i<dirtyRectsToScroll.size(); ++i) { + QRect qdirtyRect = dirtyRectsToScroll[i]; + qdirtyRect.translate(dx, dy); + update_sys(qdirtyRect); + } + + // Update newly exposed areas. This will generate new dirty areas on + // q, and therefore, we do it after updating the old dirty rects above: + if (dx != 0) + update_sys(deltaXRect); + if (dy != 0) + update_sys(deltaYRect); - NSSize deltaSize = NSMakeSize(dx, dy); - [view scrollRect:scrollRect by:deltaSize]; - [view setNeedsDisplayInRect:deltaXRect]; - [view setNeedsDisplayInRect:deltaYRect]; #endif // QT_MAC_USE_COCOA + } + + for (int i=0; i<movedChildren.size(); i++) { + QWidget *w = movedChildren.at(i); + QMoveEvent e(w->pos(), w->pos() - scrollDelta); + QApplication::sendEvent(w, &e); + } } int QWidget::metric(PaintDeviceMetric m) const @@ -4693,16 +4858,19 @@ int QWidget::metric(PaintDeviceMetric m) const case PdmWidthMM: return qRound(metric(PdmWidth) * 25.4 / qreal(metric(PdmDpiX))); case PdmHeight: - case PdmWidth: { + case PdmWidth: #ifndef QT_MAC_USE_COCOA - HIRect rect; + { HIRect rect; HIViewGetFrame(qt_mac_nativeview_for(this), &rect); -#else - NSRect rect = [qt_mac_nativeview_for(this) frame]; -#endif if(m == PdmWidth) return (int)rect.size.width; return (int)rect.size.height; } +#else + if (m == PdmWidth) + return data->crect.width(); + else + return data->crect.height(); +#endif case PdmDepth: return 32; case PdmNumColors: @@ -4817,19 +4985,35 @@ void QWidgetPrivate::registerDropSite(bool on) #endif } -void QWidgetPrivate::registerTouchWindow() +void QWidgetPrivate::registerTouchWindow(bool enable) { + Q_UNUSED(enable); +#ifdef QT_MAC_USE_COCOA #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) return; + Q_Q(QWidget); - if (!q->testAttribute(Qt::WA_WState_Created)) + if (enable == touchEventsEnabled) return; -#ifndef QT_MAC_USE_COCOA - // Needs implementation! -#else - NSView *view = qt_mac_nativeview_for(q); - [view setAcceptsTouchEvents:YES]; + + QCocoaView *view = static_cast<QCocoaView *>(qt_mac_effectiveview_for(q)); + if (!view) + return; + + if (enable) { + ++view->alienTouchCount; + if (view->alienTouchCount == 1) { + touchEventsEnabled = true; + [view setAcceptsTouchEvents:YES]; + } + } else { + --view->alienTouchCount; + if (view->alienTouchCount == 0) { + touchEventsEnabled = false; + [view setAcceptsTouchEvents:NO]; + } + } #endif #endif } @@ -4837,13 +5021,17 @@ void QWidgetPrivate::registerTouchWindow() void QWidgetPrivate::setMask_sys(const QRegion ®ion) { Q_UNUSED(region); -#ifndef QT_MAC_USE_COCOA Q_Q(QWidget); + +#ifndef QT_MAC_USE_COCOA if (q->isWindow()) ReshapeCustomWindow(qt_mac_window_for(q)); else HIViewReshapeStructure(qt_mac_nativeview_for(q)); #else + if (!q->internalWinId()) + return; + if (extra->mask.isEmpty()) { extra->maskBits = QImage(); finishCocoaMaskSetup(); @@ -4851,6 +5039,7 @@ void QWidgetPrivate::setMask_sys(const QRegion ®ion) syncCocoaMask(); } + topLevelAt_cache = 0; #endif } @@ -4928,7 +5117,7 @@ void QWidgetPrivate::finishCocoaMaskSetup() [window setOpaque:(extra->imageMask == 0)]; [window invalidateShadow]; } - qt_mac_set_needs_display(q, QRegion()); + macSetNeedsDisplay(QRegion()); } #endif diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index e37e06d..0ac66cb 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -233,6 +233,7 @@ struct QTLWExtra { #elif defined(Q_WS_QPA) QPlatformWindow *platformWindow; QPlatformWindowFormat platformWindowFormat; + quint32 screenIndex; // index in qplatformscreenlist #endif }; @@ -360,7 +361,8 @@ public: DrawInvisible = 0x08, DontSubtractOpaqueChildren = 0x10, DontSetCompositionMode = 0x20, - DontDrawOpaqueChildren = 0x40 + DontDrawOpaqueChildren = 0x40, + DontDrawNativeChildren = 0x80 }; enum CloseMode { @@ -774,6 +776,8 @@ public: void x11UpdateIsOpaque(); bool isBackgroundInherited() const; void updateX11AcceptFocus(); + QPoint mapToGlobal(const QPoint &pos) const; + QPoint mapFromGlobal(const QPoint &pos) const; #elif defined(Q_WS_WIN) // <--------------------------------------------------------- WIN uint noPaintOnScreen : 1; // see qwidget_win.cpp ::paintEngine() #ifndef QT_NO_GESTURES @@ -792,7 +796,6 @@ public: #elif defined(Q_WS_MAC) // <--------------------------------------------------------- MAC // This is new stuff uint needWindowChange : 1; - uint hasAlienChildren : 1; // Each wiget keeps a list of all its child and grandchild OpenGL widgets. // This list is used to update the gl context whenever a parent and a granparent @@ -823,6 +826,7 @@ public: void macUpdateIgnoreMouseEvents(); void macUpdateMetalAttribute(); void macUpdateIsOpaque(); + void macSetNeedsDisplay(QRegion region); void setEnabled_helper_sys(bool enable); bool isRealWindow() const; void adjustWithinMaxAndMinSize(int &w, int &h); @@ -858,6 +862,7 @@ public: bool isInUnifiedToolbar; QWindowSurface *unifiedSurface; QPoint toolbar_offset; + bool touchEventsEnabled; #endif void determineWindowClass(); void transferChildren(); @@ -870,7 +875,7 @@ public: static OSStatus qt_window_event(EventHandlerCallRef er, EventRef event, void *); static OSStatus qt_widget_event(EventHandlerCallRef er, EventRef event, void *); static bool qt_widget_rgn(QWidget *, short, RgnHandle, bool); - void registerTouchWindow(); + void registerTouchWindow(bool enable = true); #elif defined(Q_WS_QWS) // <--------------------------------------------------------- QWS void setMaxWindowState_helper(); void setFullScreenSize_helper(); @@ -885,11 +890,9 @@ public: void updateCursor() const; #endif QScreen* getScreen() const; -#elif defined(Q_WS_QPA) +#elif defined(Q_WS_QPA) // <--------------------------------------------------------- QPA void setMaxWindowState_helper(); void setFullScreenSize_helper(); - - int screenNumber; // screen the widget should be displayed on #ifndef QT_NO_CURSOR void updateCursor() const; #endif diff --git a/src/gui/kernel/qwidget_qpa.cpp b/src/gui/kernel/qwidget_qpa.cpp index 617d984..001810e 100644 --- a/src/gui/kernel/qwidget_qpa.cpp +++ b/src/gui/kernel/qwidget_qpa.cpp @@ -53,7 +53,6 @@ #include <QtGui/QPlatformCursor> QT_BEGIN_NAMESPACE -static QPlatformScreen *qt_screenForWidget(const QWidget *w); void q_createNativeChildrenAndSetParent(QPlatformWindow *parentWindow, const QWidget *parentWidget) { @@ -122,7 +121,7 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO } } - QApplicationPrivate::platformIntegration()->moveToScreen(q, screenNumber); + QApplicationPrivate::platformIntegration()->moveToScreen(q, topData()->screenIndex); // qDebug() << "create_sys" << q << q->internalWinId(); } @@ -163,7 +162,6 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) { Q_Q(QWidget); - // QWidget *oldParent = q->parentWidget(); Qt::WindowFlags oldFlags = data.window_flags; int targetScreen = -1; @@ -173,7 +171,7 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) // programmer specified desktop widget // get the desktop's screen number - targetScreen = newparent->d_func()->screenNumber; + targetScreen = newparent->window()->d_func()->topData()->screenIndex; newparent = 0; } @@ -192,7 +190,7 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) f |= Qt::Window; if (targetScreen == -1) { if (parent) - targetScreen = qobject_cast<QWidget *>(parent)->d_func()->screenNumber; + targetScreen = q->parentWidget()->window()->d_func()->topData()->screenIndex; } } @@ -215,20 +213,14 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) data.window_flags = window->setWindowFlags(data.window_flags); } - // Reparenting child to toplevel - if ((f&Qt::Window) && !(oldFlags&Qt::Window)) { - //qDebug() << "setParent_sys() change to toplevel"; - q->create(); //### too early: this ought to happen at show() time - } - - if (q->isWindow() || (!newparent || newparent->isVisible()) || explicitlyHidden) q->setAttribute(Qt::WA_WState_Hidden); q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden); // move the window to the selected screen if (!newparent && targetScreen != -1) { - screenNumber = targetScreen; + if (maybeTopData()) + maybeTopData()->screenIndex = targetScreen; // only if it is already created if (q->testAttribute(Qt::WA_WState_Created)) { QPlatformIntegration *platform = QApplicationPrivate::platformIntegration(); @@ -379,9 +371,8 @@ QWidget *QWidget::keyboardGrabber() void QWidget::activateWindow() { - // XXX -// qDebug() << "QWidget::activateWindow" << this; - QApplication::setActiveWindow(this); //##### + if (platformWindow()) + platformWindow()->requestActivateWindow(); } void QWidgetPrivate::show_sys() @@ -397,7 +388,11 @@ void QWidgetPrivate::show_sys() QPlatformWindow *window = q->platformWindow(); if (window) { - const QRect geomRect = q->geometry(); + QRect geomRect = q->geometry(); + if (!q->isWindow()) { + QPoint topLeftOfWindow = q->mapTo(q->nativeParentWidget(),QPoint()); + geomRect.moveTopLeft(topLeftOfWindow); + } const QRect windowRect = window->geometry(); if (windowRect != geomRect) { window->setGeometry(geomRect); @@ -409,9 +404,6 @@ void QWidgetPrivate::show_sys() } if (window) window->setVisible(true); - - if (q->isWindow() && q->windowType() != Qt::Popup && q->windowType() != Qt::ToolTip && !(q->windowFlags() & Qt::X11BypassWindowManagerHint)) - q->activateWindow(); //### QWindowSystemInterface should have callback function for when WS actually activates window. } } @@ -595,7 +587,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) if (q->isVisible()) { if (q->platformWindow()) { if (q->isWindow()) { - q->platformWindow()->setGeometry(q->frameGeometry()); + q->platformWindow()->setGeometry(q->geometry()); } else { QPoint posInNativeParent = q->mapTo(q->nativeParentWidget(),QPoint()); q->platformWindow()->setGeometry(QRect(posInNativeParent,r.size())); @@ -647,37 +639,11 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) scrollRect(r, dx, dy); } -static QPlatformScreen *qt_screenForWidget(const QWidget *w) -{ - if (!w) - return 0; - QRect frame = w->frameGeometry(); - if (!w->isWindow()) - frame.moveTopLeft(w->mapToGlobal(QPoint(0, 0))); - const QPoint p = (frame.topLeft() + frame.bottomRight()) / 2; - - QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); - QList<QPlatformScreen *> screens = pi->screens(); - - for (int i = 0; i < screens.size(); ++i) { - if (screens[i]->geometry().contains(p)) - return screens[i]; - } - - // Assume screen zero if we have it. - if (!screens.isEmpty()) - return screens[0]; - else - qWarning("qt_screenForWidget: no screens"); - - return 0; -} - int QWidget::metric(PaintDeviceMetric m) const { Q_D(const QWidget); - QPlatformScreen *screen = qt_screenForWidget(this); + QPlatformScreen *screen = QPlatformScreen::platformScreenForWidget(this); if (!screen) { if (m == PdmDpiX || m == PdmDpiY) return 72; @@ -798,9 +764,9 @@ void QWidgetPrivate::deleteTLSysExtra() context->deleteQGLContext(); } } + setWinId(0); delete extra->topextra->platformWindow; extra->topextra->platformWindow = 0; - extra->topextra->backingStore.destroy(); } } diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index cf23981..1a5417d 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -444,6 +444,7 @@ static QVector<Atom> getNetWmState(QWidget *w) && actualType == XA_ATOM && actualFormat == 32) { returnValue.resize(bytesLeft / 4); XFree((char*) propertyData); + propertyData = 0; // fetch all data if (XGetWindowProperty(X11->display, w->internalWinId(), ATOM(_NET_WM_STATE), 0, @@ -458,7 +459,8 @@ static QVector<Atom> getNetWmState(QWidget *w) if (!returnValue.isEmpty()) { memcpy(returnValue.data(), propertyData, returnValue.size() * sizeof(Atom)); } - XFree((char*) propertyData); + if (propertyData) + XFree((char*) propertyData); } return returnValue; @@ -1289,39 +1291,77 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) #endif } - -QPoint QWidget::mapToGlobal(const QPoint &pos) const +QPoint QWidgetPrivate::mapToGlobal(const QPoint &pos) const { - Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { - QPoint p = pos + data->crect.topLeft(); + Q_Q(const QWidget); + if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) { + QPoint p = pos + q->data->crect.topLeft(); //cannot trust that !isWindow() implies parentWidget() before create - return (isWindow() || !parentWidget()) ? p : parentWidget()->mapToGlobal(p); + return (q->isWindow() || !q->parentWidget()) ? p : q->parentWidget()->d_func()->mapToGlobal(p); } - int x, y; + int x, y; Window child; - QPoint p = d->mapToWS(pos); - XTranslateCoordinates(X11->display, internalWinId(), - QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(), + QPoint p = mapToWS(pos); + XTranslateCoordinates(X11->display, q->internalWinId(), + QApplication::desktop()->screen(xinfo.screen())->internalWinId(), p.x(), p.y(), &x, &y, &child); return QPoint(x, y); } - -QPoint QWidget::mapFromGlobal(const QPoint &pos) const +QPoint QWidgetPrivate::mapFromGlobal(const QPoint &pos) const { - Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + Q_Q(const QWidget); + if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) { //cannot trust that !isWindow() implies parentWidget() before create - QPoint p = (isWindow() || !parentWidget()) ? pos : parentWidget()->mapFromGlobal(pos); - return p - data->crect.topLeft(); + QPoint p = (q->isWindow() || !q->parentWidget()) ? pos : q->parentWidget()->d_func()->mapFromGlobal(pos); + return p - q->data->crect.topLeft(); } - int x, y; + int x, y; Window child; XTranslateCoordinates(X11->display, - QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(), - internalWinId(), pos.x(), pos.y(), &x, &y, &child); - return d->mapFromWS(QPoint(x, y)); + QApplication::desktop()->screen(xinfo.screen())->internalWinId(), + q->internalWinId(), pos.x(), pos.y(), &x, &y, &child); + return mapFromWS(QPoint(x, y)); +} + +QPoint QWidget::mapToGlobal(const QPoint &pos) const +{ + Q_D(const QWidget); + QPoint offset = data->crect.topLeft(); + const QWidget *w = this; + const QWidget *p = w->parentWidget(); + while (!w->isWindow() && p) { + w = p; + p = p->parentWidget(); + offset += w->data->crect.topLeft(); + } + + const QWidgetPrivate *wd = w->d_func(); + QTLWExtra *tlw = wd->topData(); + if (!tlw->embedded) + return pos + offset; + + return d->mapToGlobal(pos); +} + +QPoint QWidget::mapFromGlobal(const QPoint &pos) const +{ + Q_D(const QWidget); + QPoint offset = data->crect.topLeft(); + const QWidget *w = this; + const QWidget *p = w->parentWidget(); + while (!w->isWindow() && p) { + w = p; + p = p->parentWidget(); + offset += w->data->crect.topLeft(); + } + + const QWidgetPrivate *wd = w->d_func(); + QTLWExtra *tlw = wd->topData(); + if (!tlw->embedded) + return pos - offset; + + return d->mapFromGlobal(pos); } void QWidgetPrivate::updateSystemBackground() diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.cpp b/src/gui/kernel/qwindowsysteminterface_qpa.cpp index bb29cbf..b6177b0 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa.cpp +++ b/src/gui/kernel/qwindowsysteminterface_qpa.cpp @@ -82,6 +82,12 @@ void QWindowSystemInterface::handleLeaveEvent(QWidget *tlw) QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); } +void QWindowSystemInterface::handleWindowActivated(QWidget *tlw) +{ + QWindowSystemInterfacePrivate::ActivatedWindowEvent *e = new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + void QWindowSystemInterface::handleGeometryChange(QWidget *tlw, const QRect &newRect) { if (tlw) { diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.h b/src/gui/kernel/qwindowsysteminterface_qpa.h index 1c79f2a..39c2f79 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa.h +++ b/src/gui/kernel/qwindowsysteminterface_qpa.h @@ -83,6 +83,7 @@ public: static void handleCloseEvent(QWidget *w); static void handleEnterEvent(QWidget *w); static void handleLeaveEvent(QWidget *w); + static void handleWindowActivated(QWidget *w); // Changes to the screen static void handleScreenGeometryChange(int screenIndex); diff --git a/src/gui/kernel/qwindowsysteminterface_qpa_p.h b/src/gui/kernel/qwindowsysteminterface_qpa_p.h index 78e1f33..3491a1a 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa_p.h +++ b/src/gui/kernel/qwindowsysteminterface_qpa_p.h @@ -54,6 +54,7 @@ public: GeometryChange, Enter, Leave, + ActivatedWindow, Mouse, Wheel, Key, @@ -102,6 +103,14 @@ public: QWeakPointer<QWidget> leave; }; + class ActivatedWindowEvent : public WindowSystemEvent { + public: + ActivatedWindowEvent(QWidget *activatedWindow) + : WindowSystemEvent(ActivatedWindow), activated(activatedWindow) + { } + QWeakPointer<QWidget> activated; + }; + class UserEvent : public WindowSystemEvent { public: UserEvent(QWidget * w, ulong time, EventType t) diff --git a/src/gui/kernel/qx11embed_x11.cpp b/src/gui/kernel/qx11embed_x11.cpp index 47cefe4..49a8194 100644 --- a/src/gui/kernel/qx11embed_x11.cpp +++ b/src/gui/kernel/qx11embed_x11.cpp @@ -415,8 +415,9 @@ static Bool functor(Display *display, XEvent *event, XPointer arg) status = XGetWindowProperty(display, data->id, ATOM(WM_STATE), 0, 2, False, ATOM(WM_STATE), &ret, &format, &nitems, &after, &retval ); if (status == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) { - long *state = (long *)retval; - if (state[0] == WithdrawnState) { + long state = *(long *)retval; + XFree(retval); + if (state == WithdrawnState) { data->clearedWmState = true; return true; } @@ -833,6 +834,8 @@ bool QX11EmbedWidget::x11Event(XEvent *event) XUnmapWindow(x11Info().display(), internalWinId()); } } + if (prop_return) + XFree(prop_return); } } diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index dea3a72..173fb71 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -1781,7 +1781,7 @@ QColor QColor::toHsl() const color.ct.ahsl.lightness = qRound(lightness * USHRT_MAX); if (qFuzzyIsNull(delta)) { // achromatic case, hue is undefined - color.ct.ahsl.hue = 0; + color.ct.ahsl.hue = USHRT_MAX; color.ct.ahsl.saturation = 0; } else { // chromatic case diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 9928dea..5904296 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -43,7 +43,9 @@ #include <private/qpaintengine_raster_p.h> #include <private/qpainter_p.h> #include <private/qdrawhelper_x86_p.h> +#ifdef QT_HAVE_ARM_SIMD #include <private/qdrawhelper_arm_simd_p.h> +#endif #include <private/qdrawhelper_neon_p.h> #include <private/qmath_p.h> #include <qmath.h> diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index fddc4d0..3d532d2 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -636,10 +636,14 @@ void QPainterPath::moveTo(const QPointF &p) #ifdef QPP_DEBUG printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y()); #endif + + if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(p.x()) || qt_is_nan(p.y())) - qWarning("QPainterPath::moveTo: Adding point where x or y is NaN, results are undefined"); + qWarning("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call"); #endif + return; + } + ensureData(); detach(); @@ -682,10 +686,14 @@ void QPainterPath::lineTo(const QPointF &p) #ifdef QPP_DEBUG printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y()); #endif + + if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(p.x()) || qt_is_nan(p.y())) - qWarning("QPainterPath::lineTo: Adding point where x or y is NaN, results are undefined"); + qWarning("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call"); #endif + return; + } + ensureData(); detach(); @@ -737,11 +745,15 @@ void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF & printf("QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n", c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y()); #endif + + if (!qt_is_finite(c1.x()) || !qt_is_finite(c1.y()) || !qt_is_finite(c2.x()) || !qt_is_finite(c2.y()) + || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(c1.x()) || qt_is_nan(c1.y()) || qt_is_nan(c2.x()) || qt_is_nan(c2.y()) - || qt_is_nan(e.x()) || qt_is_nan(e.y())) - qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN, results are undefined"); + qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN or Inf, ignoring call"); #endif + return; + } + ensureData(); detach(); @@ -790,10 +802,14 @@ void QPainterPath::quadTo(const QPointF &c, const QPointF &e) printf("QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n", c.x(), c.y(), e.x(), e.y()); #endif + + if (!qt_is_finite(c.x()) || !qt_is_finite(c.y()) || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(c.x()) || qt_is_nan(c.y()) || qt_is_nan(e.x()) || qt_is_nan(e.y())) - qWarning("QPainterPath::quadTo: Adding point where x or y is NaN, results are undefined"); + qWarning("QPainterPath::quadTo: Adding point where x or y is NaN or Inf, ignoring call"); #endif + return; + } + ensureData(); detach(); @@ -857,11 +873,15 @@ void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength printf("QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n", rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength); #endif + + if (!qt_is_finite(rect.x()) && !qt_is_finite(rect.y()) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height()) + || !qt_is_finite(startAngle) || !qt_is_finite(sweepLength)) { #ifndef QT_NO_DEBUG - if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height()) - || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) - qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined"); + qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN or Inf, ignoring call"); #endif + return; + } + if (rect.isNull()) return; @@ -960,10 +980,13 @@ QPointF QPainterPath::currentPosition() const */ void QPainterPath::addRect(const QRectF &r) { + if (!qt_is_finite(r.x()) || !qt_is_finite(r.y()) || !qt_is_finite(r.width()) || !qt_is_finite(r.height())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(r.x()) || qt_is_nan(r.y()) || qt_is_nan(r.width()) || qt_is_nan(r.height())) - qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN, results are undefined"); + qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN or Inf, ignoring call"); #endif + return; + } + if (r.isNull()) return; @@ -1040,11 +1063,14 @@ void QPainterPath::addPolygon(const QPolygonF &polygon) */ void QPainterPath::addEllipse(const QRectF &boundingRect) { + if (!qt_is_finite(boundingRect.x()) || !qt_is_finite(boundingRect.y()) + || !qt_is_finite(boundingRect.width()) || !qt_is_finite(boundingRect.height())) { #ifndef QT_NO_DEBUG - if (qt_is_nan(boundingRect.x()) || qt_is_nan(boundingRect.y()) - || qt_is_nan(boundingRect.width()) || qt_is_nan(boundingRect.height())) - qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN, results are undefined"); + qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN or Inf, ignoring call"); #endif + return; + } + if (boundingRect.isNull()) return; @@ -2366,10 +2392,12 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p) s >> x; s >> y; Q_ASSERT(type >= 0 && type <= 3); + if (!qt_is_finite(x) || !qt_is_finite(y)) { #ifndef QT_NO_DEBUG - if (qt_is_nan(x) || qt_is_nan(y)) - qWarning("QDataStream::operator>>: Adding a NaN element to path, results are undefined"); + qWarning("QDataStream::operator>>: NaN or Inf element found in path, skipping it"); #endif + continue; + } QPainterPath::Element elm = { x, y, QPainterPath::ElementType(type) }; p.d_func()->elements.append(elm); } @@ -2895,9 +2923,12 @@ QPointF QPainterPath::pointAtPercent(qreal t) const return QPointF(); } - if (isEmpty()) + if (!d_ptr || d_ptr->elements.size() == 0) return QPointF(); + if (d_ptr->elements.size() == 1) + return d_ptr->elements.at(0); + qreal totalLength = length(); qreal curLen = 0; qreal bezierLen = 0; diff --git a/src/gui/painting/qwindowsurface_raster.cpp b/src/gui/painting/qwindowsurface_raster.cpp index d0b9239..419518ac 100644 --- a/src/gui/painting/qwindowsurface_raster.cpp +++ b/src/gui/painting/qwindowsurface_raster.cpp @@ -78,6 +78,9 @@ public: #ifdef Q_WS_X11 GC gc; +#ifndef QT_NO_MITSHM + uint needsSync : 1; +#endif #ifndef QT_NO_XRENDER uint translucentBackground : 1; #endif @@ -94,6 +97,9 @@ QRasterWindowSurface::QRasterWindowSurface(QWidget *window, bool setDefaultSurfa d_ptr->translucentBackground = X11->use_xrender && window->x11Info().depth() == 32; #endif +#ifndef QT_NO_MITHSM + d_ptr->needsSync = false; +#endif #endif d_ptr->image = 0; d_ptr->inSetGeometry = false; @@ -116,8 +122,23 @@ QPaintDevice *QRasterWindowSurface::paintDevice() return &d_ptr->image->image; } +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) +void QRasterWindowSurface::syncX() +{ + // delay writing to the backbuffer until we know for sure X is done reading from it + if (d_ptr->needsSync) { + XSync(X11->display, false); + d_ptr->needsSync = false; + } +} +#endif + void QRasterWindowSurface::beginPaint(const QRegion &rgn) { +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + syncX(); +#endif + #if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_WS_WINCE)) if (!qt_widget_private(window())->isOpaque && window()->testAttribute(Qt::WA_TranslucentBackground)) { #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) @@ -217,13 +238,13 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi if (d_ptr->image->xshmpm) { XCopyArea(X11->display, d_ptr->image->xshmpm, widget->handle(), d_ptr->gc, br.x(), br.y(), br.width(), br.height(), wbr.x(), wbr.y()); - XSync(X11->display, False); + d_ptr->needsSync = true; } else if (d_ptr->image->xshmimg) { const QImage &src = d->image->image; br = br.intersected(src.rect()); XShmPutImage(X11->display, widget->handle(), d_ptr->gc, d_ptr->image->xshmimg, br.x(), br.y(), wbr.x(), wbr.y(), br.width(), br.height(), False); - XSync(X11->display, False); + d_ptr->needsSync = true; } else #endif { @@ -282,6 +303,7 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi CGrafPtr port = GetWindowPort(qt_mac_window_for(widget)); QDBeginCGContext(port, &context); #else + QMacCocoaAutoReleasePool pool; extern CGContextRef qt_mac_graphicsContextFor(QWidget *); CGContextRef context = qt_mac_graphicsContextFor(widget); #endif @@ -391,6 +413,10 @@ bool QRasterWindowSurface::scroll(const QRegion &area, int dx, int dy) if (!d->image || d->image->image.isNull()) return false; +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + syncX(); +#endif + const QVector<QRect> rects = area.rects(); for (int i = 0; i < rects.size(); ++i) qt_scrollRectInImage(d->image->image, rects.at(i), QPoint(dx, dy)); diff --git a/src/gui/painting/qwindowsurface_raster_p.h b/src/gui/painting/qwindowsurface_raster_p.h index b6e61e1..903810b 100644 --- a/src/gui/painting/qwindowsurface_raster_p.h +++ b/src/gui/painting/qwindowsurface_raster_p.h @@ -107,6 +107,9 @@ public: bool scroll(const QRegion &area, int dx, int dy); private: +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + void syncX(); +#endif void prepareBuffer(QImage::Format format, QWidget *widget); Q_DECLARE_PRIVATE(QRasterWindowSurface) QScopedPointer<QRasterWindowSurfacePrivate> d_ptr; diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index c5e95f2e..ecc2539 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -96,7 +96,8 @@ const layoutHeader QS60StylePrivate::m_layoutHeaders[] = { {320,240,1,19,"QVGA Portrait"}, {360,640,1,19,"NHD Landscape"}, {640,360,1,19,"NHD Portrait"}, -{352,800,1,12,"E90 Landscape"} +{352,800,1,12,"E90 Landscape"}, +{480,640,1,19,"VGA Landscape"} // *** End of generated data *** }; const int QS60StylePrivate::m_numberOfLayouts = @@ -108,7 +109,8 @@ const short QS60StylePrivate::data[][MAX_PIXELMETRICS] = { {5,0,-909,0,0,1,0,2,-1,8,14,22,15,15,7,164,-909,-909,-909,19,15,2,0,0,21,8,27,28,4,4,1,-909,-909,0,7,6,0,13,23,17,17,21,21,7,115,21,0,-909,-909,-909,-909,0,0,15,1,-909,0,0,-909,15,-909,-909,-909,-909,32,21,65,27,65,3,3,5,10,15,-909,5,58,13,5,0,4,4,7,9,4,4,-909,2,-909,-909,-909,-909,6,6,3,1,106}, {7,0,-909,0,0,2,0,5,-1,25,69,46,37,37,9,258,-909,-909,-909,23,19,26,0,0,32,25,72,44,5,5,2,-909,-909,0,7,21,0,17,29,22,22,27,27,7,173,29,0,-909,-909,-909,-909,0,0,25,2,-909,0,0,-909,25,-909,-909,-909,-909,87,27,77,35,77,13,3,6,8,19,-909,7,74,19,7,0,5,5,8,12,5,5,-909,3,-909,-909,-909,-909,7,7,3,1,135}, {7,0,-909,0,0,2,0,5,-1,25,68,46,37,37,9,258,-909,-909,-909,31,19,6,0,0,32,25,60,52,5,5,2,-909,-909,0,7,32,0,17,29,22,22,27,27,7,173,29,0,-909,-909,-909,-909,0,0,26,2,-909,0,0,-909,26,-909,-909,-909,-909,87,27,96,35,96,12,3,6,8,19,-909,7,74,22,7,0,5,5,8,12,5,5,-909,3,-909,-909,-909,-909,7,7,3,1,135}, -{7,0,-909,0,0,2,0,2,-1,10,20,27,18,18,9,301,-909,-909,-909,29,18,5,0,0,35,7,32,30,5,5,2,-909,-909,0,2,8,0,16,28,21,21,26,26,2,170,26,0,-909,-909,-909,-909,0,0,21,6,-909,0,0,-909,-909,-909,-909,-909,-909,54,26,265,34,265,5,5,6,3,18,-909,7,72,19,7,0,5,6,8,11,6,5,-909,2,-909,-909,-909,-909,5,5,3,1,106} +{7,0,-909,0,0,2,0,2,-1,10,20,27,18,18,9,301,-909,-909,-909,29,18,5,0,0,35,7,32,30,5,5,2,-909,-909,0,2,8,0,16,28,21,21,26,26,2,170,26,0,-909,-909,-909,-909,0,0,21,6,-909,0,0,-909,-909,-909,-909,-909,-909,54,26,265,34,265,5,5,6,3,18,-909,7,72,19,7,0,5,6,8,11,6,5,-909,2,-909,-909,-909,-909,5,5,3,1,106}, +{9,0,-909,0,0,2,0,5,-1,34,99,76,51,51,25,352,-909,-909,-909,29,25,7,0,0,43,34,42,76,7,7,2,-909,-909,0,9,14,0,23,39,30,30,37,37,9,391,40,0,-909,-909,-909,-909,0,0,29,2,-909,0,0,-909,29,-909,-909,-909,-909,115,37,96,48,96,19,19,9,1,25,-909,9,101,24,9,0,7,7,7,16,7,7,-909,3,-909,-909,-909,-909,9,9,3,1,184} // *** End of generated data *** }; diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index 04c40aa..605872e 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -1378,12 +1378,13 @@ QPixmap QS60StylePrivate::frame(SkinFrameElements frame, const QSize &size, Skin QPixmap QS60StylePrivate::backgroundTexture() { bool createNewBackground = false; + TRect applicationRect = (static_cast<CEikAppUi*>(S60->appUi())->ApplicationRect()); if (!m_background) { createNewBackground = true; } else { //if background brush does not match screensize, re-create it - if (m_background->width() != S60->screenWidthInPixels || - m_background->height() != S60->screenHeightInPixels) { + if (m_background->width() != applicationRect.Width() || + m_background->height() != applicationRect.Height()) { delete m_background; createNewBackground = true; } @@ -1391,7 +1392,7 @@ QPixmap QS60StylePrivate::backgroundTexture() if (createNewBackground) { QPixmap background = part(QS60StyleEnums::SP_QsnBgScreen, - QSize(S60->screenWidthInPixels, S60->screenHeightInPixels), 0, SkinElementFlags()); + QSize(applicationRect.Width(), applicationRect.Height()), 0, SkinElementFlags()); m_background = new QPixmap(background); } return *m_background; @@ -1411,7 +1412,6 @@ QS60Style::QS60Style() void QS60StylePrivate::handleDynamicLayoutVariantSwitch() { clearCaches(QS60StylePrivate::CC_LayoutChange); - setBackgroundTexture(qApp); setActiveLayout(); refreshUI(); foreach (QWidget *widget, QApplication::allWidgets()) diff --git a/src/gui/text/qfont_qpa.cpp b/src/gui/text/qfont_qpa.cpp index 5fed18b..7b09b59 100644 --- a/src/gui/text/qfont_qpa.cpp +++ b/src/gui/text/qfont_qpa.cpp @@ -90,7 +90,7 @@ QString QFont::defaultFamily() const familyName = QString::fromLatin1("helvetica"); } - QStringList list = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(familyName,QFont::StyleNormal,QUnicodeTables::Common); + QStringList list = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(familyName,QFont::StyleNormal,QFont::StyleHint(d->request.styleHint),QUnicodeTables::Common); if (list.size()) { familyName = list.at(0); } diff --git a/src/gui/text/qfont_s60.cpp b/src/gui/text/qfont_s60.cpp index c7d36ef..114191d 100644 --- a/src/gui/text/qfont_s60.cpp +++ b/src/gui/text/qfont_s60.cpp @@ -49,13 +49,14 @@ QT_BEGIN_NAMESPACE #ifdef QT_NO_FREETYPE Q_GLOBAL_STATIC(QMutex, lastResortFamilyMutex); +#endif // QT_NO_FREETYPE + extern QStringList qt_symbian_fontFamiliesOnFontServer(); // qfontdatabase_s60.cpp Q_GLOBAL_STATIC_WITH_INITIALIZER(QStringList, fontFamiliesOnFontServer, { // We are only interested in the initial font families. No Application fonts. // Therefore, we are allowed to cache the list. x->append(qt_symbian_fontFamiliesOnFontServer()); }); -#endif // QT_NO_FREETYPE QString QFont::lastResortFont() const { @@ -95,7 +96,20 @@ QString QFont::lastResortFamily() const const bool isJapaneseOrChineseSystem = User::Language() == ELangJapanese || User::Language() == ELangPrcChinese; - return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":"Series 60 Sans"); + static QString family; + if (family.isEmpty()) { + QStringList families = qt_symbian_fontFamiliesOnFontServer(); + const char* const preferredFamilies[] = {"Nokia Sans S60", "Series 60 Sans"}; + for (int i = 0; i < sizeof preferredFamilies / sizeof preferredFamilies[0]; ++i) { + const QString preferredFamily = QLatin1String(preferredFamilies[i]); + if (families.contains(preferredFamily)) { + family = preferredFamily; + break; + } + } + } + + return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":family.toLatin1()); #endif // QT_NO_FREETYPE } diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 20c72dd..8e92b1a 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -646,6 +646,10 @@ public: { } ~QFontDatabasePrivate() { free(); +#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) + if (symbianExtras) + delete symbianExtras; +#endif } QtFontFamily *family(const QString &f, bool = false); void free() { @@ -654,12 +658,6 @@ public: ::free(families); families = 0; count = 0; -#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) - if (symbianExtras) { - delete symbianExtras; - symbianExtras = 0; - } -#endif // don't clear the memory fonts! } @@ -678,6 +676,10 @@ public: QVector<FONTSIGNATURE> signatures; #elif defined(Q_WS_MAC) ATSFontContainerRef handle; +#elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) + QString temporaryFileName; + TInt screenDeviceFontFileId; + TUid fontStoreFontFileUid; #endif QStringList families; }; @@ -704,7 +706,7 @@ public: #if defined(Q_WS_QWS) QDataStream *stream; #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) - const QSymbianFontDatabaseExtras *symbianExtras; + QSymbianFontDatabaseExtras *symbianExtras; #endif #if defined(Q_WS_QWS) || defined(Q_WS_QPA) QStringList fallbackFamilies; @@ -2573,6 +2575,8 @@ bool QFontDatabasePrivate::isApplicationFont(const QString &fileName) \note Adding application fonts on Unix/X11 platforms without fontconfig is currently not supported. + \note On Symbian, the font family names get truncated to a length of 20 characters. + \sa addApplicationFontFromData(), applicationFontFamilies(), removeApplicationFont() */ int QFontDatabase::addApplicationFont(const QString &fileName) @@ -2603,6 +2607,8 @@ int QFontDatabase::addApplicationFont(const QString &fileName) \bold{Note:} Adding application fonts on Unix/X11 platforms without fontconfig is currently not supported. + \note On Symbian, the font family names get truncated to a length of 20 characters. + \sa addApplicationFont(), applicationFontFamilies(), removeApplicationFont() */ int QFontDatabase::addApplicationFontFromData(const QByteArray &fontData) diff --git a/src/gui/text/qfontdatabase_qpa.cpp b/src/gui/text/qfontdatabase_qpa.cpp index e54093c..e6d99c6 100644 --- a/src/gui/text/qfontdatabase_qpa.cpp +++ b/src/gui/text/qfontdatabase_qpa.cpp @@ -80,9 +80,9 @@ Q_GUI_EXPORT void qt_registerFont(const QString &familyName, const QString &fou size->handle = handle; } -static QStringList fallbackFamilies(const QString &family, const QFont::Style &style, const QUnicodeTables::Script &script) +static QStringList fallbackFamilies(const QString &family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) { - QStringList retList = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,script); + QStringList retList = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script); QFontDatabasePrivate *db = privateDb(); QStringList::iterator i; @@ -177,7 +177,11 @@ QFontEngine *loadEngine(int script, const QFontDef &request, && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol ) { if (family && !family->askedForFallback) { - family->fallbackFamilies = fallbackFamilies(family->name,QFont::Style(style->key.style),QUnicodeTables::Script(script)); + QFont::Style fontStyle = QFont::Style(style->key.style); + QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint); + if (styleHint == QFont::AnyStyle && request.fixedPitch) + styleHint = QFont::TypeWriter; + family->fallbackFamilies = fallbackFamilies(family->name,fontStyle,styleHint,QUnicodeTables::Script(script)); family->askedForFallback = true; } @@ -287,7 +291,7 @@ QFontDatabase::findFont(int script, const QFontPrivate *fp, if (!engine) { if (!request.family.isEmpty()) { - QStringList fallbacks = fallbackFamilies(request.family,QFont::Style(request.style),QUnicodeTables::Script(script)); + QStringList fallbacks = fallbackFamilies(request.family,QFont::Style(request.style),QFont::StyleHint(request.styleHint),QUnicodeTables::Script(script)); for (int i = 0; i < fallbacks.size(); i++) { QFontDef def = request; def.family = fallbacks.at(i); diff --git a/src/gui/text/qfontdatabase_s60.cpp b/src/gui/text/qfontdatabase_s60.cpp index 6ba035e..6d3970e 100644 --- a/src/gui/text/qfontdatabase_s60.cpp +++ b/src/gui/text/qfontdatabase_s60.cpp @@ -45,6 +45,8 @@ #include "qfontengine_s60_p.h" #include "qabstractfileengine.h" #include "qdesktopservices.h" +#include "qtemporaryfile.h" +#include "qtextcodec.h" #include <private/qpixmap_s60_p.h> #include <private/qt_s60_p.h> #include "qendian.h" @@ -114,7 +116,14 @@ public: ~QSymbianFontDatabaseExtrasImplementation(); const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const; - void addFontFileToFontStore(const QFileInfo &fontFileInfo); + void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt); + static inline bool appFontLimitReached(); + TUid addFontFileToFontStore(const QFileInfo &fontFileInfo); + static void clear(); + + static inline QString tempAppFontFolder(); + static const QString appFontMarkerPrefix; + static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>' struct CFontFromFontStoreReleaser { static inline void cleanup(CFont *font) @@ -144,8 +153,75 @@ public: mutable QList<const QSymbianTypeFaceExtras *> m_extras; mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash; + mutable QSet<QString> m_applicationFontFamilies; }; +const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix = + QLatin1String("Q"); + +inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() +{ + return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\'); +} + +QString QSymbianFontDatabaseExtrasImplementation::appFontMarker() +{ + static QString result; + if (result.isEmpty()) { + quint16 id = 0; + if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { + // We are allowed to load app fonts even from previous, crashed runs + // of this application, since we can access the font tables. + const quint32 uid = RProcess().Type().MostDerived().iUid; + id = static_cast<quint16>(uid + (uid >> 16)); + } else { + // If no font table Api is available, we must not even load a font + // from a previous (crashed) run of this application. Reason: we + // won't get the font tables, they are not in the CFontStore. + // So, we use the pid, for more uniqueness. + id = static_cast<quint16>(RProcess().Id().Id()); + } + result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0')); + Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4); + } + return result; +} + +static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName) +{ + const int idLength = 3; // Keep in sync with id length in appFontMarker(). + const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix; + if (fontName.length() < prefix.length() + idLength + || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix) + return false; + // Testing if the the id is base32 data + for (int i = fontName.length() - idLength; i < fontName.length(); ++i) { + const QChar &c = fontName.at(i); + if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9') + || c >= QLatin1Char('a') && c <= QLatin1Char('v'))) + return false; + } + return true; +} + +// If fontName is an application font of this app, prepend the app font marker +QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName) +{ + QFontDatabasePrivate *db = privateDb(); + Q_ASSERT(db); + const QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + return dbExtras->m_applicationFontFamilies.contains(fontName) ? + fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker() + : fontName; +} + +static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName) +{ + return markedFontName.left(markedFontName.length() + - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length()); +} + QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation() { if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { @@ -170,10 +246,13 @@ QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementati } } -void qt_cleanup_symbianFontDatabaseExtras() +void QSymbianFontDatabaseExtrasImplementation::clear() { + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; const QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras); + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); if (!dbExtras) return; // initializeDb() has never been called if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { @@ -189,9 +268,32 @@ void qt_cleanup_symbianFontDatabaseExtras() dbExtras->m_extrasHash.clear(); } +void qt_cleanup_symbianFontDatabase() +{ + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; + + QSymbianFontDatabaseExtrasImplementation::clear(); + + if (!db->applicationFonts.isEmpty()) { + QFontDatabase::removeAllApplicationFonts(); + // We remove the left over temporary font files of Qt application. + // Active fonts are undeletable since the font server holds a handle + // on them, so we do not need to worry to delete other running + // applications' fonts. + const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()); + const QStringList filter( + QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf")); + foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter)) + QFile(ttfFile.absoluteFilePath()).remove(); + db->applicationFonts.clear(); + } +} + QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation() { - qt_cleanup_symbianFontDatabaseExtras(); + qt_cleanup_symbianFontDatabase(); if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { delete m_store; m_heap->Close(); @@ -215,9 +317,10 @@ COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont) } #endif // FNTSTORE_H_INLINES_SUPPORT_FMM -const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &typeface, +const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface, bool bold, bool italic) const { + const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface); const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic)); if (!m_extrasHash.contains(searchKey)) { TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1); @@ -263,12 +366,42 @@ const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(c return m_extrasHash.value(searchKey); } -void QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo) +void QSymbianFontDatabaseExtrasImplementation::removeAppFontData( + QFontDatabasePrivate::ApplicationFont *fnt) +{ + clear(); + if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable() + && fnt->fontStoreFontFileUid.iUid != 0) + m_store->RemoveFile(fnt->fontStoreFontFileUid); + if (!fnt->families.isEmpty()) + m_applicationFontFamilies.remove(fnt->families.first()); + if (fnt->screenDeviceFontFileId != 0) + S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); + QFile::remove(fnt->temporaryFileName); + *fnt = QFontDatabasePrivate::ApplicationFont(); +} + +bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() +{ + QFontDatabasePrivate *db = privateDb(); + if (!db) + return false; + const int maxAppFonts = 5; + int registeredAppFonts = 0; + foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts) + if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts) + return true; + return false; +} + +TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo) { Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()); const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath()); - TPtrC fontFilePtr(qt_QString2TPtrC(fontFile)); - QT_TRAP_THROWING(m_store->AddFileL(fontFilePtr)); + const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile)); + TUid fontUid = {0}; + TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr)); + return fontUid; } #else // QT_NO_FREETYPE @@ -331,11 +464,24 @@ void QFontEngineMultiS60::loadEngine(int at) Q_ASSERT(engines[at]); } -static bool addFontToScreenDevice(int screenDeviceFontIndex, - const QSymbianFontDatabaseExtrasImplementation *dbExtras) -{ +#ifdef QT_NO_FREETYPE +static bool registerScreenDeviceFont(int screenDeviceFontIndex, + const QSymbianFontDatabaseExtrasImplementation *dbExtras) +{ TTypefaceSupport typefaceSupport; S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex); + + QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); + if (qt_symbian_fontNameHasAppFontMarker(familyName)) { + const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); + if (familyName.endsWith(marker)) { + familyName = qt_symbian_appFontNameWithoutMarker(familyName); + dbExtras->m_applicationFontFamilies.insert(familyName); + } else { + return false; // This was somebody else's application font. Skip it. + } + } + CFont *font; // We have to get a font instance in order to know all the details TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11); if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone) @@ -351,7 +497,6 @@ static bool addFontToScreenDevice(int screenDeviceFontIndex, styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal; styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal; - QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); QtFontFamily *family = privateDb()->family(familyName, true); family->fixedPitch = faceAttrib.IsMonoWidth(); QtFontFoundry *foundry = family->foundry(QString(), true); @@ -381,6 +526,7 @@ static bool addFontToScreenDevice(int screenDeviceFontIndex, family->writingSystems[system] = QtFontFamily::Supported; return true; } +#endif static void initializeDb() { @@ -398,7 +544,12 @@ static void initializeDb() const QSymbianFontDatabaseExtrasImplementation *dbExtras = static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); for (int i = 0; i < numTypeFaces; i++) - addFontToScreenDevice(i, dbExtras); + registerScreenDeviceFont(i, dbExtras); + + // We have to clear/release all CFonts, here, in case one of the fonts is + // an application font of another running Qt app. Otherwise the other Qt app + // cannot remove it's application font, anymore -> "Zombie Font". + QSymbianFontDatabaseExtrasImplementation::clear(); lock.relock(); @@ -421,20 +572,386 @@ static inline void load(const QString &family = QString(), int script = -1) initializeDb(); } +struct OffsetTable { + quint32 sfntVersion; + quint16 numTables, searchRange, entrySelector, rangeShift; +}; + +struct TableRecord { + quint32 tag, checkSum, offset, length; +}; + +struct NameTableHead { + quint16 format, count, stringOffset; +}; + +struct NameRecord { + quint16 platformID, encodingID, languageID, nameID, length, offset; +}; + +static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount) +{ + quint32 result = 0; + const quint32 *ptr = reinterpret_cast<const quint32*>(data); + const quint32 *endPtr = + ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32); + while (ptr < endPtr) { + const quint32 unit32Value = *ptr++; + result += qFromBigEndian(unit32Value); + } + return result; +} + +static inline quint32 toDWordBoundary(quint32 value) +{ + return (value + 3) & ~3; +} + +static inline quint32 dWordPadding(quint32 value) +{ + return (4 - (value & 3)) & 3; +} + +static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker) +{ + const quint32 tableLength = static_cast<quint32>(table.size()); + + if (tableLength > 50000 // hard limit + || tableLength < sizeof(NameTableHead)) // corrupt name table + return false; + + const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData()); + const quint16 count = qFromBigEndian(head->count); + const quint16 stringOffset = qFromBigEndian(head->stringOffset); + if (count > 200 // hard limit + || stringOffset >= tableLength // corrupt name table + || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table + return false; + + QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader); + const QByteArray markerUtf16BE = encoder.fromUnicode(marker); + const QByteArray markerAscii = marker.toAscii(); + + QByteArray markedTable; + markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra + markedTable.append(table, stringOffset); + QByteArray markedStrings; + quint32 stringDataCount = stringOffset; + for (quint16 i = 0; i < count; ++i) { + const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i; + NameRecord *nameRecord = + reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset); + const quint16 nameID = qFromBigEndian(nameRecord->nameID); + const quint16 platformID = qFromBigEndian(nameRecord->platformID); + const quint16 encodingID = qFromBigEndian(nameRecord->encodingID); + const quint16 offset = qFromBigEndian(nameRecord->offset); + const quint16 length = qFromBigEndian(nameRecord->length); + stringDataCount += length; + if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string. + || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds + return false; + const bool needsMarker = + nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21; + const bool isUnicode = + platformID == 0 || platformID == 3 && encodingID == 1; + const QByteArray originalString = + QByteArray::fromRawData(table.constData() + stringOffset + offset, length); + QByteArray markedString; + if (needsMarker) { + const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1); + markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii); + } else { + markedString = originalString; + } + nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length())); + nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length())); + markedStrings.append(markedString); + } + markedTable.append(markedStrings); + table = markedTable; + return true; +} + +const quint32 ttfMaxFileSize = 3500000; + +static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker) +{ + const quint32 ttfChecksumNumber = 0xb1b0afba; + const quint32 alignment = 4; + const quint32 ttfLength = static_cast<quint32>(ttf.size()); + if (ttfLength > ttfMaxFileSize // hard limit + || ttfLength % alignment != 0 // ttf sizes are always factors of 4 + || ttfLength <= sizeof(OffsetTable) // ttf too short + || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid + return false; + + const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData()); + const quint16 numTables = qFromBigEndian(offsetTable->numTables); + const quint32 recordsLength = + toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord)); + if (numTables > 30 // hard limit + || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty. + return false; + + QByteArray markedTtf; + markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra + markedTtf.append(ttf.constData(), recordsLength); + + const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head' + int indexOfHeadTable = -1; + quint32 ttfDataSize = recordsLength; + typedef QPair<quint32, quint32> Range; + QList<Range> memoryRanges; + memoryRanges.reserve(numTables); + for (int i = 0; i < numTables; ++i) { + TableRecord *tableRecord = + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); + const quint32 offset = qFromBigEndian(tableRecord->offset); + const quint32 length = qFromBigEndian(tableRecord->length); + const quint32 lengthAligned = toDWordBoundary(length); + ttfDataSize += lengthAligned; + if (offset < recordsLength // must not intersect ttf header/records + || offset % alignment != 0 // must be aligned + || offset > ttfLength - alignment // table out of bounds + || offset + lengthAligned > ttfLength // table out of bounds + || ttfDataSize > ttfLength) // tables would not fit into the ttf + return false; + + foreach (const Range &range, memoryRanges) + if (offset < range.first + range.second && offset + lengthAligned > range.first) + return false; // Overlaps with another table + memoryRanges.append(Range(offset, lengthAligned)); + + quint32 checkSum = qFromBigEndian(tableRecord->checkSum); + if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) { + if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32)) + return false; // Invalid 'head' table + const quint32 *checkSumAdjustmentTag = + reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset); + const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag); + checkSum += checkSumAdjustment; + indexOfHeadTable = i; // For the ttf checksum re-calculation, later + } + if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length)) + return false; // Table checksum is invalid + + bool updateTableChecksum = false; + QByteArray table; + if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) { + table = QByteArray(ttf.constData() + offset, length); + if (!ttfMarkNameTable(table, marker)) + return false; // Name table was not markable. + updateTableChecksum = true; + } else { + table = QByteArray::fromRawData(ttf.constData() + offset, length); + } + + tableRecord->offset = qToBigEndian(markedTtf.size()); + tableRecord->length = qToBigEndian(table.size()); + markedTtf.append(table); + markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding + if (updateTableChecksum) { + TableRecord *tableRecord = // Need to recalculate, since markedTtf changed + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); + const quint32 offset = qFromBigEndian(tableRecord->offset); + const quint32 length = qFromBigEndian(tableRecord->length); + tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length)); + } + } + if (indexOfHeadTable == -1 // 'head' table is mandatory + || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian. + return false; + TableRecord *headRecord = + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord)); + quint32 *checkSumAdjustmentTag = + reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset); + *checkSumAdjustmentTag = 0; + const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count()); + *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum); + ttf = markedTtf; + return true; +} + +static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName) +{ + bool result = false; + QString ttfFileName; + QFile tempFileGuard; + QFileInfo info(fileName); + if (!data.isEmpty()) { + QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() + + QSymbianFontDatabaseExtrasImplementation::appFontMarker() + + QLatin1String("XXXXXX.ttf")); + if (!tempfile.open() || tempfile.write(data) == -1) + return false; + ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath()); + tempfile.setAutoRemove(false); + tempfile.close(); + tempFileGuard.setFileName(ttfFileName); + if (!tempFileGuard.open(QIODevice::ReadOnly)) + return false; + } else if (info.isFile()) { + ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath()); + } else { + return false; + } + + CFontStore *store = 0; + RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000); + if (heap) { + QT_TRAP_THROWING( + CleanupClosePushL(*heap); + store = CFontStore::NewL(heap); + CleanupStack::PushL(store); + COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E)); + CleanupStack::PushL(rasterizer); + store->InstallRasterizerL(rasterizer); + CleanupStack::Pop(rasterizer); + TUid fontUid = {-1}; + TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName))); + if (fontUid.iUid != -1) + result = true; + CleanupStack::PopAndDestroy(2, heap); // heap, store + ); + } + + if (tempFileGuard.isOpen()) + tempFileGuard.remove(); + + return result; +} + static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) { - Q_UNUSED(fnt); + if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() + || fnt->data.size() > ttfMaxFileSize // hard limit + || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf + || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit + return; + +// Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower). +// Therefore, not using it for now, but eventually in a later version. +// if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName)) +// return; + + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; + + if (!db->count) + initializeDb(); + + QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + if (!dbExtras) + return; + + const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); + + // The QTemporaryFile object being used in the following section must be + // destructed before letting Symbian load the TTF file. Symbian would not + // load it otherwise, because QTemporaryFile will still keep some handle + // on it. The scope is used to reduce the life time of the QTemporaryFile. + // In order to prevent other processes from modifying the file between the + // moment where the QTemporaryFile is destructed and the file is loaded by + // Symbian, we have a QFile "tempFileGuard" outside the scope which opens + // the file in ReadOnly mode while the QTemporaryFile is still alive. + QFile tempFileGuard; + { + QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() + + marker + QLatin1String("XXXXXX.ttf")); + if (!tempfile.open()) + return; + const QString tempFileName = QFileInfo(tempfile).canonicalFilePath(); + if (fnt->data.isEmpty()) { + QFile sourceFile(fnt->fileName); + if (!sourceFile.open(QIODevice::ReadOnly)) + return; + fnt->data = sourceFile.readAll(); + } + if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1) + return; + tempfile.setAutoRemove(false); + tempfile.close(); // Tempfile still keeps a file handle, forbidding write access + fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore. + tempFileGuard.setFileName(tempFileName); + if (!tempFileGuard.open(QIODevice::ReadOnly)) + return; + fnt->temporaryFileName = tempFileName; + } + + const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName); + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer(); + const TInt err = + S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId); + tempFileGuard.close(); // Did its job + const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer(); + if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device? + int fontOnServerIndex = fontsOnServerAfter.count() - 1; + for (int i = 0; i < fontsOnServerBefore.count(); i++) { + if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) { + fontOnServerIndex = i; + break; + } + } + + // Must remove all font engines with their CFonts, first. + QFontCache::instance()->clear(); + db->free(); + QSymbianFontDatabaseExtrasImplementation::clear(); + + if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) + fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName)); + + const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex); + fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName)); + if (!qt_symbian_fontNameHasAppFontMarker(appFontName) + || !registerScreenDeviceFont(fontOnServerIndex, dbExtras)) + dbExtras->removeAppFontData(fnt); + } else { + if (fnt->screenDeviceFontFileId > 0) + S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open! + QFile::remove(fnt->temporaryFileName); + *fnt = QFontDatabasePrivate::ApplicationFont(); + } + lock.relock(); } bool QFontDatabase::removeApplicationFont(int handle) { - Q_UNUSED(handle); - return false; + QMutexLocker locker(fontDatabaseMutex()); + + QFontDatabasePrivate *db = privateDb(); + if (!db || handle < 0 || handle >= db->applicationFonts.count()) + return false; + QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + if (!dbExtras) + return false; + + QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle]; + if (fnt->families.isEmpty()) + return true; // Nothing to remove. Return peacefully. + + // Must remove all font engines with their CFonts, first + QFontCache::instance()->clear(); + db->free(); + dbExtras->removeAppFontData(fnt); + + db->invalidate(); // This will just emit 'fontDatabaseChanged()' + return true; } bool QFontDatabase::removeAllApplicationFonts() { - return false; + QMutexLocker locker(fontDatabaseMutex()); + + const int applicationFontsCount = privateDb()->applicationFonts.count(); + for (int i = 0; i < applicationFontsCount; ++i) + if (!removeApplicationFont(i)) + return false; + return true; } bool QFontDatabase::supportsThreadedFontRendering() @@ -467,7 +984,7 @@ QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QF QFontDatabasePrivate *db = privateDb(); QtFontDesc desc; QList<int> blacklistedFamilies; - match(script, req, req.family, QString(), -1, &desc, blacklistedFamilies); + match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies); if (!desc.family) // falling back to application font desc.family = db->family(QApplication::font().defaultFamily()); Q_ASSERT(desc.family); diff --git a/src/gui/text/qfontengine_s60.cpp b/src/gui/text/qfontengine_s60.cpp index 4317ee6..e9b54e3 100644 --- a/src/gui/text/qfontengine_s60.cpp +++ b/src/gui/text/qfontengine_s60.cpp @@ -244,10 +244,13 @@ static inline unsigned int getChar(const QChar *str, int &i, const int len) return uc; } +extern QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName); // qfontdatabase_s60.cpp + CFont *QFontEngineS60::fontWithSize(qreal size) const { CFont *result = 0; - TFontSpec fontSpec(qt_QString2TPtrC(QFontEngine::fontDef.family), TInt(size)); + const QString family = qt_symbian_fontNameWithAppFontMarker(QFontEngine::fontDef.family); + TFontSpec fontSpec(qt_QString2TPtrC(family), TInt(size)); fontSpec.iFontStyle.SetBitmapType(EAntiAliasedGlyphBitmap); fontSpec.iFontStyle.SetPosture(QFontEngine::fontDef.style == QFont::StyleNormal?EPostureUpright:EPostureItalic); fontSpec.iFontStyle.SetStrokeWeight(QFontEngine::fontDef.weight > QFont::Normal?EStrokeWeightBold:EStrokeWeightNormal); diff --git a/src/gui/text/qplatformfontdatabase_qpa.cpp b/src/gui/text/qplatformfontdatabase_qpa.cpp index d6dff41..afe762a 100644 --- a/src/gui/text/qplatformfontdatabase_qpa.cpp +++ b/src/gui/text/qplatformfontdatabase_qpa.cpp @@ -221,10 +221,11 @@ QFontEngine *QPlatformFontDatabase::fontEngine(const QFontDef &fontDef, QUnicode /*! */ -QStringList QPlatformFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QUnicodeTables::Script &script) const +QStringList QPlatformFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const { Q_UNUSED(family); Q_UNUSED(style); + Q_UNUSED(styleHint); Q_UNUSED(script); return QStringList(); } diff --git a/src/gui/text/qplatformfontdatabase_qpa.h b/src/gui/text/qplatformfontdatabase_qpa.h index aa465ab..a1faea9 100644 --- a/src/gui/text/qplatformfontdatabase_qpa.h +++ b/src/gui/text/qplatformfontdatabase_qpa.h @@ -88,7 +88,7 @@ class Q_GUI_EXPORT QPlatformFontDatabase public: virtual void populateFontDatabase(); virtual QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); - virtual QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QUnicodeTables::Script &script) const; + virtual QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName); virtual void releaseHandle(void *handle); diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 498a432..2172f74 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -663,7 +663,8 @@ void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::O Q_ASSERT(blocks.length() == fragments.length()); - finishEdit(); + if (!blockCursorAdjustment) + finishEdit(); } void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operation op) @@ -678,6 +679,7 @@ void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operati curs->changed = true; } } + finishEdit(); } void QTextDocumentPrivate::setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode) diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index daafdd9..d3e8f2d 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -147,8 +147,8 @@ symbian { text/qfontengine_s60.cpp HEADERS += \ text/qfontengine_s60_p.h - LIBS += -lfntstr -lecom } + LIBS += -lfntstr -lecom } !qpa { diff --git a/src/gui/util/qscroller.cpp b/src/gui/util/qscroller.cpp index 2ca4a6e..d60f44e 100644 --- a/src/gui/util/qscroller.cpp +++ b/src/gui/util/qscroller.cpp @@ -83,9 +83,9 @@ bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); QDebug &operator<<(QDebug &dbg, const QScrollerPrivate::ScrollSegment &s) { - dbg << "\n Time: start:" << s.startTime << " duration:" << s.deltaTime; - dbg << "\n Pos: start:" << s.startPos << " delta:" << s.deltaPos; - dbg << "\n Curve: type:" << s.curve.type() << " max progress:" << s.maxProgress << "\n"; + dbg << "\n Time: start:" << s.startTime << " duration:" << s.deltaTime << " stop progress:" << s.stopProgress; + dbg << "\n Pos: start:" << s.startPos << " delta:" << s.deltaPos << " stop:" << s.stopPos; + dbg << "\n Curve: type:" << s.curve.type() << "\n"; return dbg; } @@ -243,7 +243,7 @@ private: intuitive user experience. The QScroller object is the object that stores the current position and - speed of the scrolling and takes care of updates. + scrolling speed and takes care of updates. QScroller can be triggered by a flick gesture \code @@ -259,20 +259,20 @@ private: scroller->scrollTo(QPointF(100, 100)); \endcode - The scrolled QObjects will be receive a QScrollPrepareEvent whenever the scroller needs to + The scrolled QObjects receive a QScrollPrepareEvent whenever the scroller needs to update its geometry information and a QScrollEvent whenever the content of the object should actually be scrolled. - The scroller uses the global QAbstractAnimation timer to generate its QScrollEvents, but this - can be slowed down with QScrollerProperties::FrameRate on a per-QScroller basis. + The scroller uses the global QAbstractAnimation timer to generate its QScrollEvents. This + can be changed with QScrollerProperties::FrameRate on a per-QScroller basis. Several examples in the \c scroller examples directory show how QScroller, QScrollEvent and the scroller gesture can be used. - Even though this kinetic scroller has a huge number of settings available via + Even though this kinetic scroller has a large number of settings available via QScrollerProperties, we recommend that you leave them all at their default, platform optimized - values. In case you really want to change them you can experiment with the \c plot example in - the \c scroller examples directory first. + values. Before changing them you can experiment with the \c plot example in + the \c scroller examples directory. \sa QScrollEvent, QScrollPrepareEvent, QScrollerProperties */ @@ -293,9 +293,9 @@ bool QScroller::hasScroller(QObject *target) /*! Returns the scroller for the given \a target. - As long as the object exist this function will always return the same QScroller. - If a QScroller does not exist yet for the \a target, it will implicitly be created. - At no point will two QScrollers be active on one object. + As long as the object exists this function will always return the same QScroller instance. + If no QScroller exists for the \a target, one will implicitly be created. + At no point more than one QScroller will be active on an object. \sa hasScroller(), target() */ @@ -324,9 +324,9 @@ const QScroller *QScroller::scroller(const QObject *target) } /*! - Returns an application wide list of currently active, i.e. state() != - QScroller::Inactive, QScroller objects. - This routine is mostly useful when writing your own gesture recognizer. + Returns an application wide list of currently active QScroller objects. + Active QScroller objects are in a state() that is not QScroller::Inactive. + This function is useful when writing your own gesture recognizer. */ QList<QScroller *> QScroller::activeScrollers() { @@ -346,8 +346,8 @@ QObject *QScroller::target() const /*! \fn QScroller::scrollerPropertiesChanged(const QScrollerProperties &newProperties); - QScroller emits this signal whenever its scroller properties have been - changed. \a newProperties are the new scroller properties. + QScroller emits this signal whenever its scroller properties change. + \a newProperties are the new scroller properties. \sa scrollerProperties */ @@ -355,7 +355,7 @@ QObject *QScroller::target() const /*! \property QScroller::scrollerProperties \brief The scroller properties of this scroller. - The properties will be used by the QScroller to determine its scrolling behaviour. + The properties are used by the QScroller to determine its scrolling behavior. */ QScrollerProperties QScroller::scrollerProperties() const { @@ -378,24 +378,25 @@ void QScroller::setScrollerProperties(const QScrollerProperties &sp) /*! - Registers a custom scroll gesture recognizer and grabs it for the \a + Registers a custom scroll gesture recognizer, grabs it for the \a target and returns the resulting gesture type. If \a scrollGestureType is - set to TouchGesture the gesture will trigger on touch events - if set to + set to TouchGesture the gesture triggers on touch events. If it is set to one of LeftMouseButtonGesture, RightMouseButtonGesture or - MiddleMouseButtonGesture it will trigger on mouse events of the + MiddleMouseButtonGesture it triggers on mouse events of the corresponding button. Only one scroll gesture can be active on a single object at the same - time, so if you call this function twice on the same object, it will + time. If you call this function twice on the same object, it will ungrab the existing gesture before grabbing the new one. - Please note: To avoid nasty side-effects, all mouse events will be - consumed while the gesture is triggered. Since the mouse press event is - not consumed, the gesture needs to also send a fake mouse release event - at the global position \c{(INT_MIN, INT_MIN)} to make sure that it - doesn't mess with the internal states of the widget that received the - mouse press in the first place (which could e.g. be a QPushButton - inside a QScrollArea). + \note To avoid unwanted side-effects, mouse events are consumed while + the gesture is triggered. Since the initial mouse press event is + not consumed, the gesture sends a fake mouse release event + at the global position \c{(INT_MIN, INT_MIN)}. This ensures that + internal states of the widget that received the original mouse press + are consistent. + + \sa ungrabGesture, grabbedGesture */ Qt::GestureType QScroller::grabGesture(QObject *target, ScrollerGestureType scrollGestureType) { @@ -437,6 +438,8 @@ Qt::GestureType QScroller::grabGesture(QObject *target, ScrollerGestureType scro /*! Returns the gesture type currently grabbed for the \a target or 0 if no gesture is grabbed. + + \sa grabGesture, ungrabGesture */ Qt::GestureType QScroller::grabbedGesture(QObject *target) { @@ -449,6 +452,9 @@ Qt::GestureType QScroller::grabbedGesture(QObject *target) /*! Ungrabs the gesture for the \a target. + Does nothing if no gesture is grabbed. + + \sa grabGesture, grabbedGesture */ void QScroller::ungrabGesture(QObject *target) { @@ -521,7 +527,7 @@ QScroller::State QScroller::state() const } /*! - Stops the scroller and resets the state back to Inactive. + Stops the scroller and resets its state back to Inactive. */ void QScroller::stop() { @@ -543,13 +549,13 @@ void QScroller::stop() } /*! - \brief Returns the pixel per meter metric for the scrolled widget. + Returns the pixel per meter metric for the scrolled widget. The value is reported for both the x and y axis separately by using a QPointF. - \note Please note that this value should be physically correct, while the actual DPI settings - that Qt returns for the display may be reported wrongly (on purpose) by the underlying - windowing system (e.g. Mac OS X or Maemo 5). + \note Please note that this value should be physically correct. The actual DPI settings + that Qt returns for the display may be reported wrongly on purpose by the underlying + windowing system, for example on Mac OS X or Maemo 5. */ QPointF QScroller::pixelPerMeter() const { @@ -574,10 +580,8 @@ QPointF QScroller::pixelPerMeter() const } /*! - \brief Returns the current velocity of the scroller. - - Returns the current scrolling velocity in meter per second when in the state Scrolling. - Returns a null velocity otherwise. + Returns the current scrolling velocity in meter per second when the state is Scrolling or Dragging. + Returns a zero velocity otherwise. The velocity is reported for both the x and y axis separately by using a QPointF. @@ -597,18 +601,17 @@ QPointF QScroller::velocity() const if (!d->xSegments.isEmpty()) { const QScrollerPrivate::ScrollSegment &s = d->xSegments.head(); - qreal progress = qreal(now - s.startTime) / (qreal(s.deltaTime) / s.maxProgress); - qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / s.maxProgress / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress); + qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime); + qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress); vel.setX(v); } if (!d->ySegments.isEmpty()) { const QScrollerPrivate::ScrollSegment &s = d->ySegments.head(); - qreal progress = qreal(now - s.startTime) / (qreal(s.deltaTime) / s.maxProgress); - qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / s.maxProgress / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress); + qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime); + qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress); vel.setY(v); } - //qScrollerDebug() << "Velocity: " << vel; return vel; } default: @@ -617,11 +620,9 @@ QPointF QScroller::velocity() const } /*! - \brief Returns the target position for the scroll movement. - - Returns the planned final position for the current scroll movement or the current - position if the scroller is not in the scrolling state. - The result is undefined when the scroller is in the inactive state. + Returns the estimated final position for the current scroll movement. + Returns the current position if the scroller state is not Scrolling. + The result is undefined when the scroller state is Inactive. The target position is in pixel. @@ -642,7 +643,7 @@ QPointF QScroller::finalPosition() const In this case the scroller might or might not overshoot. The scrolling speed will be calculated so that the given position will - be reached after a platform-defined time span (e.g. 1 second for Maemo 5). + be reached after a platform-defined time span. \a pos is given in viewport coordinates. @@ -656,7 +657,7 @@ void QScroller::scrollTo(const QPointF &pos) /*! \overload - This version will reach its destination position in \a scrollTime milli seconds. + This version will reach its destination position in \a scrollTime milliseconds. */ void QScroller::scrollTo(const QPointF &pos, int scrollTime) { @@ -704,10 +705,12 @@ void QScroller::scrollTo(const QPointF &pos, int scrollTime) In cases where it is not possible to fit the rect plus margins inside the viewport the contents are scrolled so that as much as possible is visible from \a rect. - The scrolling speed will be calculated so that the given position will - be reached after a platform-defined time span (e.g. 1 second for Maemo 5). + The scrolling speed is calculated so that the given position is reached after a platform-defined + time span. This function performs the actual scrolling by calling scrollTo(). + + \sa scrollTo */ void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin) { @@ -717,7 +720,7 @@ void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin) /*! \overload - This version will reach its destination position in \a scrollTime milli seconds. + This version will reach its destination position in \a scrollTime milliseconds. */ void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin, int scrollTime) { @@ -787,11 +790,11 @@ void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin, } /*! This function resends the QScrollPrepareEvent. - * Calling resendPrepareEvent triggers a QScrollPrepareEvent from the scroller. - * This will allow the receiver to re-set content position and content size while - * scrolling. - * Calling this function while in the Inactive state is useless as the prepare event - * is send again right before scrolling starts. + Calling resendPrepareEvent triggers a QScrollPrepareEvent from the scroller. + This allows the receiver to re-set content position and content size while + scrolling. + Calling this function while in the Inactive state is useless as the prepare event + is sent again before scrolling starts. */ void QScroller::resendPrepareEvent() { @@ -799,11 +802,10 @@ void QScroller::resendPrepareEvent() d->prepareScrolling(d->pressPosition); } -/*! Set the snap positions for the horizontal axis. - * Set the snap positions to a list of \a positions. - * This will overwrite all previously set snap positions and also a previously - * set snapping interval. - * Snapping can be deactivated by setting an empty list of positions. +/*! Set the snap positions for the horizontal axis to a list of \a positions. + This overwrites all previously set snap positions and also a previously + set snapping interval. + Snapping can be deactivated by setting an empty list of positions. */ void QScroller::setSnapPositionsX(const QList<qreal> &positions) { @@ -814,13 +816,12 @@ void QScroller::setSnapPositionsX(const QList<qreal> &positions) d->recalcScrollingSegments(); } -/*! Set the snap positions for the horizontal axis. - * Set the snap positions to regular spaced intervals. - * The first snap position will be at \a first from the beginning of the list. The next at \a first + \a interval and so on. - * This can be used to implement a list header. - * This will overwrite all previously set snap positions and also a previously - * set snapping interval. - * Snapping can be deactivated by setting an interval of 0.0 +/*! Set the snap positions for the horizontal axis to regular spaced intervals. + The first snap position is at \a first. The next at \a first + \a interval. + This can be used to implement a list header. + This overwrites all previously set snap positions and also a previously + set snapping interval. + Snapping can be deactivated by setting an interval of 0.0 */ void QScroller::setSnapPositionsX(qreal first, qreal interval) { @@ -832,11 +833,10 @@ void QScroller::setSnapPositionsX(qreal first, qreal interval) d->recalcScrollingSegments(); } -/*! Set the snap positions for the vertical axis. - * Set the snap positions to a list of \a positions. - * This will overwrite all previously set snap positions and also a previously - * set snapping interval. - * Snapping can be deactivated by setting an empty list of positions. +/*! Set the snap positions for the vertical axis to a list of \a positions. + This overwrites all previously set snap positions and also a previously + set snapping interval. + Snapping can be deactivated by setting an empty list of positions. */ void QScroller::setSnapPositionsY(const QList<qreal> &positions) { @@ -847,12 +847,11 @@ void QScroller::setSnapPositionsY(const QList<qreal> &positions) d->recalcScrollingSegments(); } -/*! Set the snap positions for the vertical axis. - * Set the snap positions to regular spaced intervals. - * The first snap position will be at \a first. The next at \a first + \a interval and so on. - * This will overwrite all previously set snap positions and also a previously - * set snapping interval. - * Snapping can be deactivated by setting an interval of 0.0 +/*! Set the snap positions for the vertical axis to regular spaced intervals. + The first snap position is at \a first. The next at \a first + \a interval. + This overwrites all previously set snap positions and also a previously + set snapping interval. + Snapping can be deactivated by setting an interval of 0.0 */ void QScroller::setSnapPositionsY(qreal first, qreal interval) { @@ -951,14 +950,15 @@ void QScrollerPrivate::timerTick() /*! This function is used by gesture recognizers to inform the scroller about a new input event. - The scroller will change its internal state() according to the input event and its attached - scroller properties. Since the scroller doesn't care about the actual kind of input device the - event came from, you need to decompose the event into the \a input type, a \a position and a + The scroller changes its internal state() according to the input event and its attached + scroller properties. The scroller doesn't distinguish between the kind of input device the + event came from. Therefore the event needs to be split into the \a input type, a \a position and a milli-second \a timestamp. The \a position needs to be in the target's coordinate system. + The return value is \c true if the event should be consumed by the calling filter or \c false if the event should be forwarded to the control. - \note Using grabGesture() should be sufficient for most use cases though. + \note Using grabGesture() should be sufficient for most use cases. */ bool QScroller::handleInput(Input input, const QPointF &position, qint64 timestamp) { @@ -1005,9 +1005,18 @@ QPointF QScrollerPrivate::realDpi(int screen) return QPointF(260, 260); # elif defined(Q_WS_X11) && !defined(QT_NO_XRANDR) - if (X11->use_xrandr && X11->ptrXRRSizes) { + if (X11 && X11->use_xrandr && X11->ptrXRRSizes && X11->ptrXRRRootToScreen) { int nsizes = 0; - XRRScreenSize *sizes = X11->ptrXRRSizes(X11->display, screen == -1 ? X11->defaultScreen : screen, &nsizes); + // QDesktopWidget is based on Xinerama screens, which do not always + // correspond to RandR screens: NVidia's TwinView e.g. will show up + // as 2 screens in QDesktopWidget, but libXRandR will only see 1 screen. + // (although with the combined size of the Xinerama screens). + // Additionally, libXrandr will simply crash when calling XRRSizes + // for (the non-existent) screen 1 in this scenario. + Window root = RootWindow(X11->display, screen == -1 ? X11->defaultScreen : screen); + int randrscreen = (root != XNone) ? X11->ptrXRRRootToScreen(X11->display, root) : -1; + + XRRScreenSize *sizes = X11->ptrXRRSizes(X11->display, randrscreen == -1 ? 0 : randrscreen, &nsizes); if (nsizes > 0 && sizes && sizes->width && sizes->height && sizes->mwidth && sizes->mheight) { qScrollerDebug() << "XRandR DPI:" << QPointF(qreal(25.4) * qreal(sizes->width) / qreal(sizes->mwidth), qreal(25.4) * qreal(sizes->height) / qreal(sizes->mheight)); @@ -1073,8 +1082,23 @@ void QScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint64 delta deltaPixel = deltaPixelRaw * qreal(2.5) * ppm / 1000 / (deltaPixelRaw / qreal(deltaTime)).manhattanLength(); QPointF newv = -deltaPixel / qreal(deltaTime) * qreal(1000) / ppm; - if (releaseVelocity != QPointF(0, 0)) - newv = newv * sp->dragVelocitySmoothingFactor + releaseVelocity * (qreal(1) - sp->dragVelocitySmoothingFactor); + // around 95% of all updates are in the [1..50] ms range, so make sure + // to scale the smoothing factor over that range: this way a 50ms update + // will have full impact, while 5ms update will only have a 10% impact. + qreal smoothing = sp->dragVelocitySmoothingFactor * qMin(qreal(deltaTime), qreal(50)) / qreal(50); + + // only smooth if we already have a release velocity and only if the + // user hasn't stopped to move his finger for more than 100ms + if ((releaseVelocity != QPointF(0, 0)) && (deltaTime < 100)) { + qScrollerDebug() << "SMOOTHED from " << newv << " to " << newv * smoothing + releaseVelocity * (qreal(1) - smoothing); + // smooth x or y only if the new velocity is either 0 or at least in + // the same direction of the release velocity + if (!newv.x() || (qSign(releaseVelocity.x()) == qSign(newv.x()))) + newv.setX(newv.x() * smoothing + releaseVelocity.x() * (qreal(1) - smoothing)); + if (!newv.y() || (qSign(releaseVelocity.y()) == qSign(newv.y()))) + newv.setY(newv.y() * smoothing + releaseVelocity.y() * (qreal(1) - smoothing)); + } else + qScrollerDebug() << "NO SMOOTHING to " << newv; releaseVelocity.setX(qBound(-sp->maximumVelocity, newv.x(), sp->maximumVelocity)); releaseVelocity.setY(qBound(-sp->maximumVelocity, newv.y(), sp->maximumVelocity)); @@ -1082,23 +1106,24 @@ void QScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint64 delta qScrollerDebug() << " --> new velocity:" << releaseVelocity; } -void QScrollerPrivate::pushSegment(ScrollType type, qreal deltaTime, qreal startPos, qreal endPos, QEasingCurve::Type curve, Qt::Orientation orientation, qreal maxProgress) +void QScrollerPrivate::pushSegment(ScrollType type, qreal deltaTime, qreal stopProgress, qreal startPos, qreal deltaPos, qreal stopPos, QEasingCurve::Type curve, Qt::Orientation orientation) { - if (startPos == endPos) + if (startPos == stopPos || deltaPos == 0) return; ScrollSegment s; if (orientation == Qt::Horizontal && !xSegments.isEmpty()) - s.startTime = xSegments.last().startTime + xSegments.last().deltaTime; + s.startTime = xSegments.last().startTime + xSegments.last().deltaTime * xSegments.last().stopProgress; else if (orientation == Qt::Vertical && !ySegments.isEmpty()) - s.startTime = ySegments.last().startTime + ySegments.last().deltaTime; + s.startTime = ySegments.last().startTime + ySegments.last().deltaTime * ySegments.last().stopProgress; else s.startTime = monotonicTimer.elapsed(); s.startPos = startPos; - s.deltaPos = endPos - startPos; + s.deltaPos = deltaPos; + s.stopPos = stopPos; s.deltaTime = deltaTime * 1000; - s.maxProgress = maxProgress; + s.stopProgress = stopProgress; s.curve.setType(curve); s.type = type; @@ -1133,23 +1158,17 @@ void QScrollerPrivate::recalcScrollingSegments(bool forceRecalc) */ qreal QScrollerPrivate::scrollingSegmentsEndPos(Qt::Orientation orientation) const { - const QQueue<ScrollSegment> *segments; - qreal endPos; - if (orientation == Qt::Horizontal) { - segments = &xSegments; - endPos = contentPosition.x() + overshootPosition.x(); + if (xSegments.isEmpty()) + return contentPosition.x() + overshootPosition.x(); + else + return xSegments.last().stopPos; } else { - segments = &ySegments; - endPos = contentPosition.y() + overshootPosition.y(); - } - - if (!segments->isEmpty()) { - const ScrollSegment &last = segments->last(); - endPos = last.startPos + last.deltaPos; + if (ySegments.isEmpty()) + return contentPosition.y() + overshootPosition.y(); + else + return ySegments.last().stopPos; } - - return endPos; } /*! \internal @@ -1175,23 +1194,23 @@ bool QScrollerPrivate::scrollingSegmentsValid(Qt::Orientation orientation) return true; const ScrollSegment &last = segments->last(); - qreal endPos = last.startPos + last.deltaPos; + qreal stopPos = last.stopPos; if (last.type == ScrollTypeScrollTo) return true; // scrollTo is always valid if (last.type == ScrollTypeOvershoot && - endPos != minPos && endPos != maxPos) + (stopPos != minPos && stopPos != maxPos)) return false; - if (endPos < minPos || endPos > maxPos) + if (stopPos < minPos || stopPos > maxPos) return false; - if (endPos == minPos || endPos == maxPos) // the begin and the end of the list are always ok + if (stopPos == minPos || stopPos == maxPos) // the begin and the end of the list are always ok return true; - qreal nextSnap = nextSnapPos(endPos, 0, orientation); - if (!qIsNaN(nextSnap) && endPos != nextSnap) + qreal nextSnap = nextSnapPos(stopPos, 0, orientation); + if (!qIsNaN(nextSnap) && stopPos != nextSnap) return false; return true; @@ -1204,11 +1223,10 @@ void QScrollerPrivate::createScrollToSegments(qreal v, qreal deltaTime, qreal en { Q_UNUSED(v); - if (orientation == Qt::Horizontal) { + if (orientation == Qt::Horizontal) xSegments.clear(); - } else { + else ySegments.clear(); - } qScrollerDebug() << "+++ createScrollToSegments: t:" << deltaTime << "ep:" << endPos << "o:" << int(orientation); @@ -1216,10 +1234,10 @@ void QScrollerPrivate::createScrollToSegments(qreal v, qreal deltaTime, qreal en qreal startPos = (orientation == Qt::Horizontal) ? contentPosition.x() + overshootPosition.x() : contentPosition.y() + overshootPosition.y(); - qreal deltaPos = endPos - startPos; + qreal deltaPos = (endPos - startPos) / 2; - pushSegment(type, deltaTime * 0.3, startPos, startPos + deltaPos * 0.5, QEasingCurve::InQuad, orientation); - pushSegment(type, deltaTime * 0.7, startPos + deltaPos * 0.5, endPos, sp->scrollingCurve.type(), orientation); + pushSegment(type, deltaTime * qreal(0.3), qreal(1.0), startPos, deltaPos, startPos + deltaPos, QEasingCurve::InQuad, orientation); + pushSegment(type, deltaTime * qreal(0.7), qreal(1.0), startPos + deltaPos, deltaPos, endPos, sp->scrollingCurve.type(), orientation); } /*! \internal @@ -1253,17 +1271,6 @@ void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal pp qScrollerDebug() << "+++ createScrollingSegments: s:" << startPos << "maxPos:" << maxPos << "o:" << int(orientation); - // -- check if we are in overshoot - if (startPos < minPos) { - createScrollToSegments(v, sp->overshootScrollTime * 0.5, minPos, orientation, ScrollTypeOvershoot); - return; - } - - if (startPos > maxPos) { - createScrollToSegments(v, sp->overshootScrollTime * 0.5, maxPos, orientation, ScrollTypeOvershoot); - return; - } - qScrollerDebug() << "v = " << v << ", decelerationFactor = " << sp->decelerationFactor << ", curveType = " << sp->scrollingCurve.type(); // This is only correct for QEasingCurve::OutQuad (linear velocity, @@ -1299,6 +1306,16 @@ void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal pp if (nextSnap < lowerSnapPos || qIsNaN(lowerSnapPos)) lowerSnapPos = nextSnap; + // -- check if are in overshoot and end in overshoot + if ((startPos < minPos && endPos < minPos) || + (startPos > maxPos && endPos > maxPos)) { + qreal stopPos = endPos < minPos ? minPos : maxPos; + qreal oDeltaTime = sp->overshootScrollTime; + + pushSegment(ScrollTypeOvershoot, oDeltaTime * qreal(0.7), qreal(1.0), startPos, stopPos - startPos, stopPos, sp->scrollingCurve.type(), orientation); + return; + } + if (qAbs(v) < sp->minimumVelocity) { qScrollerDebug() << "### below minimum Vel" << orientation; @@ -1324,8 +1341,9 @@ void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal pp endPos = higherSnapPos; deltaPos = endPos - startPos; - pushSegment(ScrollTypeFlick, sp->snapTime * 0.3, startPos, startPos + deltaPos * 0.3, QEasingCurve::InQuad, orientation); - pushSegment(ScrollTypeFlick, sp->snapTime * 0.7, startPos + deltaPos * 0.3, endPos, sp->scrollingCurve.type(), orientation); + qreal midPos = startPos + deltaPos * qreal(0.3); + pushSegment(ScrollTypeFlick, sp->snapTime * qreal(0.3), qreal(1.0), startPos, midPos - startPos, midPos, QEasingCurve::InQuad, orientation); + pushSegment(ScrollTypeFlick, sp->snapTime * qreal(0.7), qreal(1.0), midPos, endPos - midPos, endPos, sp->scrollingCurve.type(), orientation); return; } @@ -1352,32 +1370,38 @@ void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal pp qScrollerDebug() << "Overshoot: delta:" << (stopPos - startPos); - qreal maxProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos - startPos) / deltaPos)); - qScrollerDebug() << "Overshoot maxp:" << maxProgress; - - pushSegment(ScrollTypeFlick, deltaTime * maxProgress, startPos, stopPos, sp->scrollingCurve.type(), orientation, maxProgress); + qreal stopProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos - startPos) / deltaPos)); - if (canOvershoot) { - qreal endV = qSign(v) * deltaTime * sp->decelerationFactor * qreal(0.5) * differentialForProgress(sp->scrollingCurve, maxProgress); - qScrollerDebug() << "Overshoot: velocity" << endV; - qScrollerDebug() << "Overshoot: maxVelocity" << sp->maximumVelocity; - qScrollerDebug() << "Overshoot: viewsize" << viewSize; - qScrollerDebug() << "Overshoot: factor" << sp->overshootScrollDistanceFactor; + if (!canOvershoot) { + qScrollerDebug() << "Overshoot stopp:" << stopProgress; - qreal oDistance = viewSize * sp->overshootScrollDistanceFactor * endV / sp->maximumVelocity; + pushSegment(ScrollTypeFlick, deltaTime, stopProgress, startPos, endPos, stopPos, sp->scrollingCurve.type(), orientation); + } else { qreal oDeltaTime = sp->overshootScrollTime; + qreal oStopProgress = qMin(stopProgress + oDeltaTime * qreal(0.3) / deltaTime, qreal(1)); + qreal oDistance = startPos + deltaPos * sp->scrollingCurve.valueForProgress(oStopProgress) - stopPos; + qreal oMaxDistance = qSign(oDistance) * (viewSize * sp->overshootScrollDistanceFactor); + + qScrollerDebug() << "1 oDistance:" << oDistance << "Max:" << oMaxDistance << "stopP/oStopP" << stopProgress << oStopProgress; - pushSegment(ScrollTypeOvershoot, oDeltaTime * 0.3, stopPos, stopPos + oDistance, sp->scrollingCurve.type(), orientation); - pushSegment(ScrollTypeOvershoot, oDeltaTime * 0.7, stopPos + oDistance, stopPos, sp->scrollingCurve.type(), orientation); + if (qAbs(oDistance) > qAbs(oMaxDistance)) { + oStopProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos + oMaxDistance - startPos) / deltaPos)); + oDistance = oMaxDistance; + qScrollerDebug() << "2 oDistance:" << oDistance << "Max:" << oMaxDistance << "stopP/oStopP" << stopProgress << oStopProgress; + } + + pushSegment(ScrollTypeFlick, deltaTime, oStopProgress, startPos, deltaPos, stopPos + oDistance, sp->scrollingCurve.type(), orientation); + pushSegment(ScrollTypeOvershoot, oDeltaTime * qreal(0.7), qreal(1.0), stopPos + oDistance, -oDistance, stopPos, sp->scrollingCurve.type(), orientation); } return; } - pushSegment(ScrollTypeFlick, deltaTime, startPos, endPos, sp->scrollingCurve.type(), orientation); + pushSegment(ScrollTypeFlick, deltaTime, qreal(1.0), startPos, deltaPos, endPos, sp->scrollingCurve.type(), orientation); } -/*! Prepares scrolling by sending a QScrollPrepareEvent to the receiver widget. +/*! \internal + Prepares scrolling by sending a QScrollPrepareEvent to the receiver widget. Returns true if the scrolling was accepted and a target was returned. */ bool QScrollerPrivate::prepareScrolling(const QPointF &position) @@ -1796,13 +1820,18 @@ qreal QScrollerPrivate::nextSegmentPosition(QQueue<ScrollSegment> &segments, qin while (!segments.isEmpty()) { const ScrollSegment s = segments.head(); - if ((s.startTime + s.deltaTime) <= now) { + if ((s.startTime + s.deltaTime * s.stopProgress) <= now) { segments.dequeue(); - pos = s.startPos + s.deltaPos; + pos = s.stopPos; } else if (s.startTime <= now) { - qreal progress = qreal(now - s.startTime) / (qreal(s.deltaTime) / s.maxProgress); - pos = s.startPos + s.deltaPos * s.curve.valueForProgress(progress) / s.curve.valueForProgress(s.maxProgress); - break; + qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime); + pos = s.startPos + s.deltaPos * s.curve.valueForProgress(progress); + if (s.deltaPos > 0 ? pos > s.stopPos : pos < s.stopPos) { + segments.dequeue(); + pos = s.stopPos; + } else { + break; + } } else { break; } @@ -1835,12 +1864,12 @@ void QScrollerPrivate::setContentPositionHelperScrolling() } /*! \internal - * Returns the next snap point in direction. - * If \a direction >0 it will return the next snap point that is larger than the current position. - * If \a direction <0 it will return the next snap point that is smaller than the current position. - * If \a direction ==0 it will return the nearest snap point (or the current position if we are already - * on a snap point. - * Returns the nearest snap position or NaN if no such point could be found. + Returns the next snap point in direction. + If \a direction >0 it will return the next snap point that is larger than the current position. + If \a direction <0 it will return the next snap point that is smaller than the current position. + If \a direction ==0 it will return the nearest snap point (or the current position if we are already + on a snap point. + Returns the nearest snap position or NaN if no such point could be found. */ qreal QScrollerPrivate::nextSnapPos(qreal p, int dir, Qt::Orientation orientation) { @@ -1962,7 +1991,7 @@ qreal QScrollerPrivate::nextSnapPos(qreal p, int dir, Qt::Orientation orientatio This enum contains the different QScroller states. \value Inactive The scroller is not scrolling and nothing is pressed. - \value Pressed A touch event was received or the mouse button pressed but the scroll area is currently not dragged. + \value Pressed A touch event was received or the mouse button was pressed but the scroll area is currently not dragged. \value Dragging The scroll area is currently following the touch point or mouse. \value Scrolling The scroll area is moving on it's own. */ @@ -1973,8 +2002,8 @@ qreal QScrollerPrivate::nextSnapPos(qreal p, int dir, Qt::Orientation orientatio This enum contains the different gesture types that are supported by the QScroller gesture recognizer. \value TouchGesture The gesture recognizer will only trigger on touch - events. Specifically it will react on single touch points when using a - touch screen and dual touch points when using a touchpad. + events. Specifically it will react on single touch points when using a + touch screen and dual touch points when using a touchpad. \value LeftMouseButtonGesture The gesture recognizer will only trigger on left mouse button events. \value MiddleMouseButtonGesture The gesture recognizer will only trigger on middle mouse button events. \value RightMouseButtonGesture The gesture recognizer will only trigger on right mouse button events. diff --git a/src/gui/util/qscroller_p.h b/src/gui/util/qscroller_p.h index 98f34f7..d16eef9 100644 --- a/src/gui/util/qscroller_p.h +++ b/src/gui/util/qscroller_p.h @@ -98,7 +98,8 @@ public: qreal startPos; qreal deltaPos; QEasingCurve curve; - qreal maxProgress; + qreal stopProgress; // whatever is.. + qreal stopPos; // ..reached first ScrollType type; }; @@ -122,7 +123,7 @@ public: void setDpiFromWidget(QWidget *widget); void updateVelocity(const QPointF &deltaPixelRaw, qint64 deltaTime); - void pushSegment(ScrollType type, qreal deltaTime, qreal startPos, qreal endPos, QEasingCurve::Type curve, Qt::Orientation orientation, qreal maxProgress = 1.0); + void pushSegment(ScrollType type, qreal deltaTime, qreal stopProgress, qreal startPos, qreal deltaPos, qreal stopPos, QEasingCurve::Type curve, Qt::Orientation orientation); void recalcScrollingSegments(bool forceRecalc = false); qreal scrollingSegmentsEndPos(Qt::Orientation orientation) const; bool scrollingSegmentsValid(Qt::Orientation orientation); diff --git a/src/gui/util/qscrollerproperties.cpp b/src/gui/util/qscrollerproperties.cpp index 2e52959..b159e05 100644 --- a/src/gui/util/qscrollerproperties.cpp +++ b/src/gui/util/qscrollerproperties.cpp @@ -95,11 +95,11 @@ QScrollerPropertiesPrivate *QScrollerPropertiesPrivate::defaults() The QScrollerProperties class stores the parameters used by QScroller. - The default settings are platform dependant and Qt will emulate the + The default settings are platform dependent so that Qt emulates the platform behaviour for kinetic scrolling. As a convention the QScrollerProperties are in physical units (meter, - seconds) and will be converted by QScroller using the current DPI. + seconds) and are converted by QScroller using the current DPI. \sa QScroller */ @@ -179,10 +179,10 @@ bool QScrollerPropertiesPrivate::operator==(const QScrollerPropertiesPrivate &p) } /*! - Sets the scroller properties returned by the default constructor to \a sp. + Sets the scroller properties for all new QScrollerProperties objects to \a sp. Use this function to override the platform default properties returned by the default - constructor. If you only want to change the scroller properties of a single scroller, then use + constructor. If you only want to change the scroller properties of a single scroller, use QScroller::setScrollerProperties() \note Calling this function will not change the content of already existing @@ -291,50 +291,48 @@ void QScrollerProperties::setScrollMetric(ScrollMetric metric, const QVariant &v This enum describes the various modes of overshooting. - \value OvershootWhenScrollable Overshooting is when the content is scrollable. This is the + \value OvershootWhenScrollable Overshooting is possible when the content is scrollable. This is the default. - \value OvershootAlwaysOff Overshooting is never enabled (even when the content is scrollable). + \value OvershootAlwaysOff Overshooting is never enabled, even when the content is scrollable. - \value OvershootAlwaysOn Overshooting is always enabled (even when the content is not - scrollable). + \value OvershootAlwaysOn Overshooting is always enabled, even when the content is not + scrollable. */ /*! \enum QScrollerProperties::ScrollMetric This enum contains the different scroll metric types. When not indicated otherwise the - setScrollMetric function expects a QVariant of a real value. + setScrollMetric function expects a QVariant of type qreal. - See the QScroller documentation for a further explanation of the concepts behind the different + See the QScroller documentation for further details of the concepts behind the different values. - \value MousePressEventDelay This is the time a mouse press event will be delayed when starting + \value MousePressEventDelay This is the time a mouse press event is delayed when starting a flick gesture in \c{[s]}. If the gesture is triggered within that time, no mouse press or - release will be sent to the scrolled object. If it triggers after that delay the (delayed) - mouse press plus a faked release event (at global postion \c{QPoint(-QWIDGETSIZE_MAX, - -QWIDGETSIZE_MAX)} will be sent. If the gesture is canceled, then both the (delayed) mouse - press plus the real release event will be delivered. + release is sent to the scrolled object. If it triggers after that delay the delayed + mouse press plus a faked release event at global postion \c{QPoint(-QWIDGETSIZE_MAX, + -QWIDGETSIZE_MAX)} is sent. If the gesture is canceled, then both the delayed mouse + press plus the real release event are delivered. \value DragStartDistance This is the minimum distance the touch or mouse point needs to be moved before the flick gesture is triggered in \c m. - \value DragVelocitySmoothingFactor A value that describes how much new drag velocities are + \value DragVelocitySmoothingFactor A value that describes to which extent new drag velocities are included in the final scrolling velocity. This value should be in the range between \c 0 and - \c 1. The lower the value, the more smoothing will be applied to the dragging velocity. The - default value is \c 0.8. + \c 1. The lower the value, the more smoothing is applied to the dragging velocity. - \value AxisLockThreshold If greater than zero a scroll movement will be restricted to one axis - only if the movement is inside an angle about the axis. The threshold must be in the range \c 0 - to \c 1. + \value AxisLockThreshold Restricts the movement to one axis if the movement is inside an angle + around the axis. The threshold must be in the range \c 0 to \c 1. \value ScrollingCurve The QEasingCurve used when decelerating the scrolling velocity after an user initiated flick. Please note that this is the easing curve for the positions, \bold{not} - the velocity: the default is QEasingCurve::OutQuad, which results is a linear decrease in + the velocity: the default is QEasingCurve::OutQuad, which results in a linear decrease in velocity (1st derivative) and a constant deceleration (2nd derivative). \value DecelerationFactor This factor influences how long it takes the scroller to decelerate - to 0 velocity. The actual value heavily depends on the chosen ScrollingCurve, but for most + to 0 velocity. The actual value depends on the chosen ScrollingCurve. For most types the value should be in the range from \c 0.1 to \c 2.0 \value MinimumVelocity The minimum velocity that is needed after ending the touch or releasing @@ -344,39 +342,38 @@ void QScrollerProperties::setScrollMetric(ScrollMetric metric, const QVariant &v \value MaximumClickThroughVelocity This is the maximum allowed scroll speed for a click-through in \c{m/s}. This means that a click on a currently (slowly) scrolling object will not only stop - the scrolling but the click event will also be delivered to the UI control - this is very + the scrolling but the click event will also be delivered to the UI control. This is useful when using exponential-type scrolling curves. \value AcceleratingFlickMaximumTime This is the maximum time in \c seconds that a flick gesture - can take to be recognized as an accelerating flick. If set to zero no such gesture will be + can take to be recognized as an accelerating flick. If set to zero no such gesture is detected. An "accelerating flick" is a flick gesture executed on an already scrolling object. In such cases the scrolling speed is multiplied by AcceleratingFlickSpeedupFactor in order to accelerate it. - \value AcceleratingFlickSpeedupFactor The current speed will be multiplied by this number if an - accelerating flick is detected. Should be \c{> 1}. + \value AcceleratingFlickSpeedupFactor The current speed is multiplied by this number if an + accelerating flick is detected. Should be \c{>= 1}. \value SnapPositionRatio This is the distance that the user must drag the area beween two snap - points in order to snap it to the next position. e.g. \c{0.33} means that the scroll must only + points in order to snap it to the next position. \c{0.33} means that the scroll must only reach one third of the distance between two snap points to snap to the next one. The ratio must - be in the range \c 0 to \c 1. + be between \c 0 and \c 1. \value SnapTime This is the time factor for the scrolling curve. A lower value means that the scrolling will take longer. The scrolling distance is independet of this value. \value OvershootDragResistanceFactor This value is the factor between the mouse dragging and - the actual scroll area movement (during overshoot). The factor must be in the range \c 0 to \c - 1. + the actual scroll area movement (during overshoot). The factor must be between \c 0 and \c 1. \value OvershootDragDistanceFactor This is the maximum distance for overshoot movements while - dragging. The actual overshoot distance will be calculated by multiplying this value with the - viewport size of the scrolled object. The factor must be in the range \c 0 to \c 1. + dragging. The actual overshoot distance is calculated by multiplying this value with the + viewport size of the scrolled object. The factor must be between \c 0 and \c 1. \value OvershootScrollDistanceFactor This is the maximum distance for overshoot movements while - scrolling. The actual overshoot distance will be calculated by multiplying this value with the - viewport size of the scrolled object. The factor must be in the range \c 0 to \c 1. + scrolling. The actual overshoot distance is calculated by multiplying this value with the + viewport size of the scrolled object. The factor must be between \c 0 and \c 1. - \value OvershootScrollTime This is the time in \c seconds that will be used to play the + \value OvershootScrollTime This is the time in \c seconds that is used to play the complete overshoot animation. \value HorizontalOvershootPolicy This is the horizontal overshooting policy (see OvershootPolicy). @@ -385,13 +382,12 @@ void QScrollerProperties::setScrollMetric(ScrollMetric metric, const QVariant &v \value FrameRate This is the frame rate which should be used while dragging or scrolling. QScroller uses a QAbstractAnimation timer internally to sync all scrolling operations to other - animations that might be active at the same time. If the Standard value of 60 frames per - second is too fast for your use case, you can lower the frames per second with this setting - (while still being in-sync with QAbstractAnimation). Please note that only the values of the + animations that might be active at the same time. If the standard value of 60 frames per + second is too fast, it can be lowered with this setting, + while still being in-sync with QAbstractAnimation. Please note that only the values of the FrameRates enum are allowed here. - \value ScrollMetricCount This is just used when enumerating the metrics. It is always the last - entry. + \value ScrollMetricCount This is always the last entry. */ QT_END_NAMESPACE diff --git a/src/gui/widgets/qcocoamenu_mac.mm b/src/gui/widgets/qcocoamenu_mac.mm index d5c8eb8..dff37b4 100644 --- a/src/gui/widgets/qcocoamenu_mac.mm +++ b/src/gui/widgets/qcocoamenu_mac.mm @@ -48,6 +48,7 @@ #include <private/qt_cocoa_helpers_mac_p.h> #include <private/qapplication_p.h> #include <private/qaction_p.h> +#include <private/qcocoaapplication_mac_p.h> #include <QtGui/QMenu> diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index 7946137..b5dda5a 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -2486,10 +2486,18 @@ void QComboBox::showPopup() listRect.setWidth(listRect.height()); //by default popup is centered on screen in landscape listRect.moveCenter(screen.center()); - if (staConTopRect.IsEmpty() && AknLayoutUtils::CbaLocation() != AknLayoutUtils::EAknCbaLocationBottom) { - // landscape without stacon, menu should be at the right - (opt.direction == Qt::LeftToRight) ? listRect.setRight(screen.right()) : - listRect.setLeft(screen.left()); + if (staConTopRect.IsEmpty()) { + TRect cbaRect = TRect(); + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EControlPane, cbaRect); + AknLayoutUtils::TAknCbaLocation cbaLocation = AknLayoutUtils::CbaLocation(); + switch (cbaLocation) { + case AknLayoutUtils::EAknCbaLocationRight: + listRect.setRight(screen.right()); + break; + case AknLayoutUtils::EAknCbaLocationLeft: + listRect.setLeft(screen.left()); + break; + } } } #endif diff --git a/src/gui/widgets/qfocusframe.cpp b/src/gui/widgets/qfocusframe.cpp index c159a1e..6508c31 100644 --- a/src/gui/widgets/qfocusframe.cpp +++ b/src/gui/widgets/qfocusframe.cpp @@ -85,6 +85,9 @@ void QFocusFramePrivate::update() void QFocusFramePrivate::updateSize() { Q_Q(QFocusFrame); + if (!widget) + return; + int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin), hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin); QPoint pos(widget->x(), widget->y()); diff --git a/src/gui/widgets/qmainwindow.cpp b/src/gui/widgets/qmainwindow.cpp index a786e2f..fd61783 100644 --- a/src/gui/widgets/qmainwindow.cpp +++ b/src/gui/widgets/qmainwindow.cpp @@ -1516,11 +1516,6 @@ void QMainWindow::setUnifiedTitleAndToolBarOnMac(bool set) if (!isWindow() || d->useHIToolBar == set || QSysInfo::MacintoshVersion < QSysInfo::MV_10_3) return; - // ### Disable when using alien widgets - if (testAttribute(Qt::WA_NativeWindow) == false) { - return; - } - d->useHIToolBar = set; createWinId(); // We need the hiview for down below. diff --git a/src/gui/widgets/qmainwindowlayout_mac.mm b/src/gui/widgets/qmainwindowlayout_mac.mm index 9a23ca7..e428ffc 100644 --- a/src/gui/widgets/qmainwindowlayout_mac.mm +++ b/src/gui/widgets/qmainwindowlayout_mac.mm @@ -355,7 +355,11 @@ void QMainWindowLayout::updateHIToolBarStatus() // Move everything out of the HIToolbar into the main toolbar. while (!qtoolbarsInUnifiedToolbarList.isEmpty()) { // Should shrink the list by one every time. - layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, qtoolbarsInUnifiedToolbarList.first()); + QToolBar *toolbar = qtoolbarsInUnifiedToolbarList.first(); + layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar); +#if defined(QT_MAC_USE_COCOA) + toolbar->d_func()->isInUnifiedToolbar = false; +#endif } macWindowToolbarSet(qt_mac_window_for(layoutState.mainWindow), 0); } else { @@ -363,7 +367,8 @@ void QMainWindowLayout::updateHIToolBarStatus() for (int i = 0; i < toolbars.size(); ++i) { QToolBar *toolbar = toolbars.at(i); if (toolBarArea(toolbar) == Qt::TopToolBarArea) { - removeWidget(toolbar); // Do this here, because we are in an in-between state. + // Do this here, because we are in an in-between state. + removeWidget(toolbar); layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar); } } @@ -387,10 +392,20 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar if (toolbar == 0) return; +#if defined(QT_MAC_USE_COCOA) + // toolbar will now become native (if not allready) since we need + // an nsview for it inside the corresponding NSToolbarItem. + // Setting isInUnifiedToolbar will (among other things) stop alien + // siblings from becoming native when this happends since the toolbar + // will not overlap with other children of the QMainWindow. NB: Switching + // unified toolbar off after this stage is not supported, as this means + // that either the menubar must be alien again, or the sibling must + // be backed by an nsview to protect from overlapping issues: + toolbar->d_func()->isInUnifiedToolbar = true; +#endif QToolBarLayout *toolbarLayout = static_cast<QToolBarLayout *>(toolbar->layout()); - toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(), - toolbar->maximumSize())); + toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(), toolbar->maximumSize())); if (toolbarLayout->hasExpandFlag() == false) toolbar->setMaximumSize(toolbar->sizeHint()); @@ -399,8 +414,8 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar toolbarLayout->setUsePopupMenu(true); // Make the toolbar a child of the mainwindow to avoid creating a window. toolbar->setParent(layoutState.mainWindow); - toolbar->createWinId(); // Now create the OSViewRef. + toolbar->winId(); // Now create the OSViewRef. layoutState.mainWindow->createWinId(); OSWindowRef window = qt_mac_window_for(layoutState.mainWindow); diff --git a/src/gui/widgets/qmenubar.cpp b/src/gui/widgets/qmenubar.cpp index dda5aba..5bfac9a 100644 --- a/src/gui/widgets/qmenubar.cpp +++ b/src/gui/widgets/qmenubar.cpp @@ -1073,8 +1073,11 @@ void QMenuBar::paintEvent(QPaintEvent *e) void QMenuBar::setVisible(bool visible) { #if defined(Q_WS_MAC) || defined(Q_OS_WINCE) || defined(Q_WS_S60) - if (isNativeMenuBar()) + if (isNativeMenuBar()) { + if (!visible) + QWidget::setVisible(false); return; + } #endif QWidget::setVisible(visible); } diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp index bad010f..e34e6bb 100644 --- a/src/network/access/qnetworkaccessftpbackend.cpp +++ b/src/network/access/qnetworkaccessftpbackend.cpp @@ -307,8 +307,10 @@ void QNetworkAccessFtpBackend::ftpDone() // logged in successfully, send the stat requests (if supported) QString command = url().path(); command.prepend(QLatin1String("%1 ")); - if (supportsSize) + if (supportsSize) { + ftp->rawCommand(QLatin1String("TYPE I")); sizeId = ftp->rawCommand(command.arg(QLatin1String("SIZE"))); // get size + } if (supportsMdtm) mdtmId = ftp->rawCommand(command.arg(QLatin1String("MDTM"))); // get modified time if (!supportsSize && !supportsMdtm) diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 27b7945..8f050d4 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -48,6 +48,7 @@ #include "qabstractnetworkcache.h" #include "QtNetwork/qnetworksession.h" +#include "QtNetwork/private/qsharednetworksession_p.h" #include "qnetworkaccesshttpbackend_p.h" #include "qnetworkaccessftpbackend_p.h" @@ -1343,11 +1344,8 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co initializeSession = false; - if (networkSession) - delete networkSession; - if (!config.isValid()) { - networkSession = 0; + networkSession.clear(); online = false; if (networkAccessible == QNetworkAccessManager::NotAccessible) @@ -1358,18 +1356,12 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co return; } - networkSession = new QNetworkSession(config, q); + networkSession = QSharedNetworkSessionManager::getSession(config); - QObject::connect(networkSession, SIGNAL(opened()), q, SIGNAL(networkSessionConnected())); - QObject::connect(networkSession, SIGNAL(closed()), q, SLOT(_q_networkSessionClosed())); - QObject::connect(networkSession, SIGNAL(stateChanged(QNetworkSession::State)), + QObject::connect(networkSession.data(), SIGNAL(opened()), q, SIGNAL(networkSessionConnected())); + QObject::connect(networkSession.data(), SIGNAL(closed()), q, SLOT(_q_networkSessionClosed())); + QObject::connect(networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)), q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State))); - QObject::connect(networkSession, SIGNAL(newConfigurationActivated()), - q, SLOT(_q_networkSessionNewConfigurationActivated())); - QObject::connect(networkSession, - SIGNAL(preferredConfigurationChanged(QNetworkConfiguration,bool)), - q, - SLOT(_q_networkSessionPreferredConfigurationChanged(QNetworkConfiguration,bool))); _q_networkSessionStateChanged(networkSession->state()); } @@ -1379,32 +1371,16 @@ void QNetworkAccessManagerPrivate::_q_networkSessionClosed() if (networkSession) { networkConfiguration = networkSession->configuration().identifier(); - networkSession->deleteLater(); - networkSession = 0; - } -} - -void QNetworkAccessManagerPrivate::_q_networkSessionNewConfigurationActivated() -{ - Q_Q(QNetworkAccessManager); - - if (networkSession) { - networkSession->accept(); - - emit q->networkSessionConnected(); + networkSession.clear(); } } -void QNetworkAccessManagerPrivate::_q_networkSessionPreferredConfigurationChanged(const QNetworkConfiguration &, bool) -{ - if (networkSession) - networkSession->migrate(); -} - void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession::State state) { Q_Q(QNetworkAccessManager); + if (state == QNetworkSession::Connected) + emit q->networkSessionConnected(); if (online) { if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) { online = false; diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h index f3e502c..7ef009f 100644 --- a/src/network/access/qnetworkaccessmanager.h +++ b/src/network/access/qnetworkaccessmanager.h @@ -161,8 +161,6 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>)) #if !defined(QT_NO_BEARERMANAGEMENT) && !defined(QT_MOBILITY_BEARER) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionClosed()) - Q_PRIVATE_SLOT(d_func(), void _q_networkSessionNewConfigurationActivated()) - Q_PRIVATE_SLOT(d_func(), void _q_networkSessionPreferredConfigurationChanged(QNetworkConfiguration,bool)) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State)) #endif }; diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h index ee16c25..cf4d2f3 100644 --- a/src/network/access/qnetworkaccessmanager_p.h +++ b/src/network/access/qnetworkaccessmanager_p.h @@ -128,7 +128,7 @@ public: #endif #ifndef QT_NO_BEARERMANAGEMENT - QNetworkSession *networkSession; + QSharedPointer<QNetworkSession> networkSession; QString networkConfiguration; QNetworkAccessManager::NetworkAccessibility networkAccessible; bool online; diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 50dbcc2..41a6c62 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -97,7 +97,7 @@ void QNetworkReplyImplPrivate::_q_startOperation() // state changes. state = WaitingForSession; - QNetworkSession *session = manager->d_func()->networkSession; + QNetworkSession *session = manager->d_func()->networkSession.data(); if (session) { Q_Q(QNetworkReplyImpl); @@ -115,7 +115,7 @@ void QNetworkReplyImplPrivate::_q_startOperation() } #endif - if (backend->isSynchronous()) { + if (backend && backend->isSynchronous()) { state = Finished; q_func()->setFinished(true); } else { @@ -257,7 +257,7 @@ void QNetworkReplyImplPrivate::_q_networkSessionConnected() if (manager.isNull()) return; - QNetworkSession *session = manager->d_func()->networkSession; + QNetworkSession *session = manager->d_func()->networkSession.data(); if (!session) return; @@ -307,7 +307,7 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const // in QtWebKit. QVariant synchronousHttpAttribute = req.attribute( static_cast<QNetworkRequest::Attribute>(QNetworkRequest::DownloadBufferAttribute + 1)); - if (synchronousHttpAttribute.toBool()) { + if (backend && synchronousHttpAttribute.toBool()) { backend->setSynchronous(true); if (outgoingData && outgoingData->isSequential()) { outgoingDataBuffer = new QRingBuffer(); @@ -362,7 +362,7 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); } #else - if (backend->isSynchronous()) + if (backend && backend->isSynchronous()) _q_startOperation(); else QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); @@ -693,7 +693,7 @@ void QNetworkReplyImplPrivate::finished() if (!manager.isNull()) { #ifndef QT_NO_BEARERMANAGEMENT - QNetworkSession *session = manager->d_func()->networkSession; + QNetworkSession *session = manager->d_func()->networkSession.data(); if (session && session->state() == QNetworkSession::Roaming && state == Working && errorCode != QNetworkReply::OperationCanceledError) { // only content with a known size will fail with a temporary network failure error diff --git a/src/network/bearer/bearer.pri b/src/network/bearer/bearer.pri index bc5b0b5..d58d5ec 100644 --- a/src/network/bearer/bearer.pri +++ b/src/network/bearer/bearer.pri @@ -7,11 +7,13 @@ HEADERS += bearer/qnetworkconfiguration.h \ bearer/qnetworkconfiguration_p.h \ bearer/qnetworksession_p.h \ bearer/qbearerengine_p.h \ - bearer/qbearerplugin_p.h + bearer/qbearerplugin_p.h \ + bearer/qsharednetworksession_p.h SOURCES += bearer/qnetworksession.cpp \ bearer/qnetworkconfigmanager.cpp \ bearer/qnetworkconfiguration.cpp \ bearer/qnetworkconfigmanager_p.cpp \ bearer/qbearerengine.cpp \ - bearer/qbearerplugin.cpp + bearer/qbearerplugin.cpp \ + bearer/qsharednetworksession.cpp diff --git a/src/network/bearer/qsharednetworksession.cpp b/src/network/bearer/qsharednetworksession.cpp new file mode 100644 index 0000000..51b3a32 --- /dev/null +++ b/src/network/bearer/qsharednetworksession.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsharednetworksession_p.h" +#include "qbearerengine_p.h" +#include <QThreadStorage> + +#ifndef QT_NO_BEARERMANAGEMENT + +QT_BEGIN_NAMESPACE + +QThreadStorage<QSharedNetworkSessionManager *> tls; + +inline QSharedNetworkSessionManager* sharedNetworkSessionManager() +{ + QSharedNetworkSessionManager* rv = tls.localData(); + if (!rv) { + rv = new QSharedNetworkSessionManager; + tls.setLocalData(rv); + } + return rv; +} + +QSharedPointer<QNetworkSession> QSharedNetworkSessionManager::getSession(QNetworkConfiguration config) +{ + QSharedNetworkSessionManager *m(sharedNetworkSessionManager()); + //if already have a session, return it + if (m->sessions.contains(config)) { + QSharedPointer<QNetworkSession> p = m->sessions.value(config).toStrongRef(); + if (!p.isNull()) + return p; + } + //otherwise make one + QSharedPointer<QNetworkSession> session(new QNetworkSession(config)); + m->sessions[config] = session; + return session; +} + +void QSharedNetworkSessionManager::setSession(QNetworkConfiguration config, QSharedPointer<QNetworkSession> session) +{ + QSharedNetworkSessionManager *m(sharedNetworkSessionManager()); + m->sessions[config] = session; +} + +uint qHash(const QNetworkConfiguration& config) +{ + return ((uint)config.type()) | (((uint)config.bearerType()) << 8) | (((uint)config.purpose()) << 16); +} + +QT_END_NAMESPACE + +#endif // QT_NO_BEARERMANAGEMENT diff --git a/src/network/bearer/qsharednetworksession_p.h b/src/network/bearer/qsharednetworksession_p.h new file mode 100644 index 0000000..dc84166 --- /dev/null +++ b/src/network/bearer/qsharednetworksession_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHAREDNETWORKSESSIONPRIVATE_H +#define QSHAREDNETWORKSESSIONPRIVATE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworksession.h" +#include "qnetworkconfiguration.h" +#include <QHash> +#include <QSharedPointer> +#include <QWeakPointer> +#include <QMutex> + +#ifndef QT_NO_BEARERMANAGEMENT + +QT_BEGIN_NAMESPACE + +class QSharedNetworkSessionManager +{ +public: + static QSharedPointer<QNetworkSession> getSession(QNetworkConfiguration config); + static void setSession(QNetworkConfiguration config, QSharedPointer<QNetworkSession> session); +private: + QHash<QNetworkConfiguration, QWeakPointer<QNetworkSession> > sessions; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_BEARERMANAGEMENT + +#endif //QSHAREDNETWORKSESSIONPRIVATE_H + diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp index 73143e1..d61d3b7 100644 --- a/src/network/kernel/qauthenticator.cpp +++ b/src/network/kernel/qauthenticator.cpp @@ -213,24 +213,6 @@ void QAuthenticator::setUser(const QString &user) int separatorPosn = 0; switch(d->method) { - case QAuthenticatorPrivate::DigestMd5: - if((separatorPosn = user.indexOf(QLatin1String("\\"))) != -1) { - //domain name is present - d->userDomain.clear(); - d->realm = user.left(separatorPosn); - d->user = user.mid(separatorPosn + 1); - } else if((separatorPosn = user.indexOf(QLatin1String("@"))) != -1) { - //domain name is present - d->userDomain.clear(); - d->realm = user.mid(separatorPosn + 1); - d->user = user.left(separatorPosn); - } else { - d->user = user; - d->realm.clear(); - d->userDomain.clear(); - } - break; - case QAuthenticatorPrivate::Ntlm: if((separatorPosn = user.indexOf(QLatin1String("\\"))) != -1) { //domain name is present @@ -253,6 +235,7 @@ void QAuthenticator::setUser(const QString &user) break; default: d->user = user; + d->userDomain.clear(); break; } } diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 0c18778..bbb75bc 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -102,7 +102,7 @@ extern Q_GUI_EXPORT bool qt_cleartype_enabled; extern bool qt_applefontsmoothing_enabled; #endif -Q_DECL_IMPORT extern QImage qt_imageForBrush(int brushStyle, bool invert); +Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); ////////////////////////////////// Private Methods ////////////////////////////////////////// @@ -337,7 +337,13 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() matrix.translate(brushOrigin.x(), brushOrigin.y()); QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y()); - QTransform gl_to_qt(1, 0, 0, -1, 0, height); + qreal m22 = -1; + qreal dy = height; + if (device->isFlipped()) { + m22 = 1; + dy = 0; + } + QTransform gl_to_qt(1, 0, 0, m22, 0, dy); QTransform inv_matrix; if (style == Qt::TexturePattern && textureInvertedY == -1) inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush.texture().height()) * brushQTransform * matrix).inverted() * translate; @@ -376,10 +382,16 @@ void QGL2PaintEngineExPrivate::updateMatrix() // NOTE: The resultant matrix is also transposed, as GL expects column-major matracies const GLfloat wfactor = 2.0f / width; - const GLfloat hfactor = -2.0f / height; + GLfloat hfactor = -2.0f / height; + GLfloat dx = transform.dx(); GLfloat dy = transform.dy(); + if (device->isFlipped()) { + hfactor *= -1; + dy -= height; + } + // Non-integer translates can have strange effects for some rendering operations such as // anti-aliased text rendering. In such cases, we snap the translate to the pixel grid. if (snapToPixelGrid && transform.type() == QTransform::TxTranslate) { @@ -1162,7 +1174,7 @@ void QGL2PaintEngineEx::fill(const QVectorPath &path, const QBrush &brush) d->fill(path); } -extern Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp +Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); // qtransform.cpp void QGL2PaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) @@ -1491,7 +1503,7 @@ namespace { { public: QOpenGLStaticTextUserData() - : QStaticTextUserData(OpenGLUserData), cacheSize(0, 0) + : QStaticTextUserData(OpenGLUserData), cacheSize(0, 0), cacheSerialNumber(0) { } @@ -1503,6 +1515,7 @@ namespace { QGL2PEXVertexArray vertexCoordinateArray; QGL2PEXVertexArray textureCoordinateArray; QFontEngineGlyphCache::Type glyphType; + int cacheSerialNumber; }; } @@ -1526,8 +1539,6 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp staticTextItem->fontEngine()->setGlyphCache(cacheKey, cache); cache->insert(ctx, cache); recreateVertexArrays = true; - } else if (cache->context() == 0) { // Old context has been destroyed, new context has same ptr value - cache->setContext(ctx); } if (staticTextItem->userDataNeedsUpdate) { @@ -1538,8 +1549,11 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp recreateVertexArrays = true; } else { QOpenGLStaticTextUserData *userData = static_cast<QOpenGLStaticTextUserData *>(staticTextItem->userData()); - if (userData->glyphType != glyphType) + if (userData->glyphType != glyphType) { recreateVertexArrays = true; + } else if (userData->cacheSerialNumber != cache->serialNumber()) { + recreateVertexArrays = true; + } } // We only need to update the cache with new glyphs if we are actually going to recreate the vertex arrays. @@ -1580,6 +1594,7 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp } userData->glyphType = glyphType; + userData->cacheSerialNumber = cache->serialNumber(); // Use cache if backend optimizations is turned on vertexCoordinates = &userData->vertexCoordinateArray; @@ -2063,7 +2078,10 @@ void QGL2PaintEngineExPrivate::setScissor(const QRect &rect) { const int left = rect.left(); const int width = rect.width(); - const int bottom = height - (rect.top() + rect.height()); + int bottom = height - (rect.top() + rect.height()); + if (device->isFlipped()) { + bottom = rect.top(); + } const int height = rect.height(); glScissor(left, bottom, width, height); diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index eb38947..4362c0a 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -53,12 +53,15 @@ QT_BEGIN_NAMESPACE extern Q_GUI_EXPORT bool qt_cleartype_enabled; #endif +QBasicAtomicInt qgltextureglyphcache_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1); + QGLTextureGlyphCache::QGLTextureGlyphCache(const QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix) : QImageTextureGlyphCache(type, matrix), QGLContextGroupResourceBase() , ctx(0) , pex(0) , m_blitProgram(0) , m_filterMode(Nearest) + , m_serialNumber(qgltextureglyphcache_serial_number.fetchAndAddRelaxed(1)) { #ifdef QT_GL_TEXTURE_GLYPH_CACHE_DEBUG qDebug(" -> QGLTextureGlyphCache() %p for context %p.", this, ctx); diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index c5c8985..cc6de28 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -133,6 +133,8 @@ public: void setContext(const QGLContext *context); inline const QGLContext *context() const { return ctx; } + inline int serialNumber() const { return m_serialNumber; } + enum FilterMode { Nearest, Linear @@ -152,6 +154,8 @@ private: GLfloat m_vertexCoordinateArray[8]; GLfloat m_textureCoordinateArray[8]; + + int m_serialNumber; }; QT_END_NAMESPACE diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 8b587cf..08ae774 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -163,6 +163,13 @@ symbian { HEADERS += qgl_egl_p.h + contains(QT_CONFIG, freetype) { + DEFINES += QT_NO_FONTCONFIG + INCLUDEPATH += \ + ../3rdparty/freetype/src \ + ../3rdparty/freetype/include + } + symbian:TARGET.UID3 = 0x2002131A } diff --git a/src/opengl/opengl.pro.user.2.1pre1 b/src/opengl/opengl.pro.user.2.1pre1 new file mode 100644 index 0000000..0c38724 --- /dev/null +++ b/src/opengl/opengl.pro.user.2.1pre1 @@ -0,0 +1,83 @@ +<!DOCTYPE QtCreatorProject> +<qtcreator> + <data> + <variable>ProjectExplorer.Project.ActiveTarget</variable> + <value type="int">0</value> + </data> + <data> + <variable>ProjectExplorer.Project.EditorSettings</variable> + <valuemap type="QVariantMap"> + <value key="EditorConfiguration.Codec" type="QByteArray">UTF-8</value> + </valuemap> + </data> + <data> + <variable>ProjectExplorer.Project.Target.0</variable> + <valuemap type="QVariantMap"> + <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Desktop</value> + <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Target.DesktopTarget</value> + <value key="ProjectExplorer.Target.ActiveBuildConfiguration" type="int">0</value> + <value key="ProjectExplorer.Target.ActiveRunConfiguration" type="int">0</value> + <valuemap key="ProjectExplorer.Target.BuildConfiguration.0" type="QVariantMap"> + <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.0" type="QVariantMap"> + <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">qmake</value> + <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">QtProjectManager.QMakeBuildStep</value> + <valuelist key="QtProjectManager.QMakeBuildStep.QMakeArguments" type="QVariantList"> + <value type="QString">QMAKE_ABSOLUTE_SOURCE_PATH=/home/jlind/dev/qt/lighthouse-master/src/opengl</value> + </valuelist> + </valuemap> + <valuemap key="ProjectExplorer.BuildConfiguration.BuildStep.1" type="QVariantMap"> + <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value> + <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value> + <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">false</value> + <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList"> + <value type="QString">-j9</value> + </valuelist> + <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value> + </valuemap> + <value key="ProjectExplorer.BuildConfiguration.BuildStepsCount" type="int">2</value> + <valuemap key="ProjectExplorer.BuildConfiguration.CleanStep.0" type="QVariantMap"> + <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Make</value> + <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.MakeStep</value> + <value key="Qt4ProjectManager.MakeStep.Clean" type="bool">true</value> + <valuelist key="Qt4ProjectManager.MakeStep.MakeArguments" type="QVariantList"> + <value type="QString">clean</value> + </valuelist> + <value key="Qt4ProjectManager.MakeStep.MakeCommand" type="QString"></value> + </valuemap> + <value key="ProjectExplorer.BuildConfiguration.CleanStepsCount" type="int">1</value> + <value key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment" type="bool">false</value> + <valuelist key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges" type="QVariantList"/> + <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">Debug</value> + <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4BuildConfiguration</value> + <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration" type="int">2</value> + <value key="Qt4ProjectManager.Qt4BuildConfiguration.BuildDirectory" type="QString">/home/jlind/builds/master-lighthouse/src/opengl</value> + <value key="Qt4ProjectManager.Qt4BuildConfiguration.QtVersionId" type="int">23</value> + <value key="Qt4ProjectManager.Qt4BuildConfiguration.ToolChain" type="int">0</value> + <value key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild" type="bool">true</value> + </valuemap> + <value key="ProjectExplorer.Target.BuildConfigurationCount" type="int">1</value> + <valuemap key="ProjectExplorer.Target.RunConfiguration.0" type="QVariantMap"> + <value key="ProjectExplorer.ProjectConfiguration.DisplayName" type="QString">headers</value> + <value key="ProjectExplorer.ProjectConfiguration.Id" type="QString">Qt4ProjectManager.Qt4RunConfiguration</value> + <value key="Qt4ProjectManager.Qt4RunConfiguration.BaseEnvironmentBase" type="int">2</value> + <valuelist key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments" type="QVariantList"/> + <value key="Qt4ProjectManager.Qt4RunConfiguration.ProFile" type="QString">../../../../../builds/master-lighthouse/include/QtOpenGL/headers.pri</value> + <value key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix" type="bool">false</value> + <value key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal" type="bool">false</value> + <valuelist key="Qt4ProjectManager.Qt4RunConfiguration.UserEnvironmentChanges" type="QVariantList"/> + <value key="Qt4ProjectManager.Qt4RunConfiguration.UserSetName" type="bool">false</value> + <value key="Qt4ProjectManager.Qt4RunConfiguration.UserSetWorkingDirectory" type="bool">false</value> + <value key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory" type="QString"></value> + </valuemap> + <value key="ProjectExplorer.Target.RunConfigurationCount" type="int">1</value> + </valuemap> + </data> + <data> + <variable>ProjectExplorer.Project.TargetCount</variable> + <value type="int">1</value> + </data> + <data> + <variable>ProjectExplorer.Project.Updater.FileVersion</variable> + <value type="int">4</value> + </data> +</qtcreator> diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index be2da4a..0ec3a15 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -3772,24 +3772,7 @@ QGLWidget::QGLWidget(QWidget *parent, const QGLWidget* shareWidget, Qt::WindowFl setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); setAutoFillBackground(true); // for compatibility -#ifdef Q_WS_QPA - QPlatformWindowFormat platformFormat = QGLFormat::toPlatformWindowFormat(QGLFormat::defaultFormat()); - platformFormat.setUseDefaultSharedContext(false); - if (shareWidget && shareWidget->d_func()->glcx) { - QPlatformGLContext *sharedPlatformContext = shareWidget->d_func()->glcx->d_func()->platformContext; - platformFormat.setSharedContext(sharedPlatformContext); - } - setPlatformWindowFormat(platformFormat); - winId(); // create window; - QGLContext *glContext = 0; - if (platformWindow()) - glContext = QGLContext::fromPlatformGLContext(platformWindow()->glContext()); - if (glContext){ - d->init(glContext,shareWidget); - } -#else d->init(new QGLContext(QGLFormat::defaultFormat(), this), shareWidget); -#endif } @@ -3829,24 +3812,7 @@ QGLWidget::QGLWidget(const QGLFormat &format, QWidget *parent, const QGLWidget* setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground); setAutoFillBackground(true); // for compatibility -#ifdef Q_WS_QPA - QPlatformWindowFormat platformFormat = QGLFormat::toPlatformWindowFormat(format); - platformFormat.setUseDefaultSharedContext(false); - if (shareWidget && shareWidget->d_func()->glcx) { - QPlatformGLContext *sharedPlatformContext = shareWidget->d_func()->glcx->d_func()->platformContext; - platformFormat.setSharedContext(sharedPlatformContext); - } - setPlatformWindowFormat(platformFormat); - winId(); // create window; - QGLContext *glContext = 0; - if (platformWindow()) - glContext = QGLContext::fromPlatformGLContext(platformWindow()->glContext()); - if (glContext){ - d->init(glContext,shareWidget); - } -#else d->init(new QGLContext(format, this), shareWidget); -#endif } /*! diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 204abe8..5508598 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -373,6 +373,7 @@ public: #if defined(Q_WS_QPA) QPlatformGLContext *platformContext; + void setupSharing(); #elif defined(Q_WS_X11) || defined(Q_WS_MAC) void* cx; #endif diff --git a/src/opengl/qgl_qpa.cpp b/src/opengl/qgl_qpa.cpp index 415e915..0f4b305 100644 --- a/src/opengl/qgl_qpa.cpp +++ b/src/opengl/qgl_qpa.cpp @@ -115,6 +115,16 @@ QPlatformWindowFormat QGLFormat::toPlatformWindowFormat(const QGLFormat &format) return retFormat; } +void QGLContextPrivate::setupSharing() { + Q_Q(QGLContext); + QPlatformGLContext *sharedPlatformGLContext = platformContext->platformWindowFormat().sharedGLContext(); + if (sharedPlatformGLContext) { + QGLContext *actualSharedContext = QGLContext::fromPlatformGLContext(sharedPlatformGLContext); + sharing = true; + QGLContextGroup::addShare(q,actualSharedContext); + } +} + bool QGLFormat::hasOpenGL() { return QApplicationPrivate::platformIntegration()->hasOpenGL(); @@ -151,19 +161,9 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) if (d->valid) { d->platformContext->setQGLContextHandle(this,qDeleteQGLContext); } + d->setupSharing(); } - if (d->valid) { - QPlatformGLContext *sharedPlatformGLContext = d->platformContext->platformWindowFormat().sharedGLContext(); - if (sharedPlatformGLContext) { - QGLContext *actualSharedContext = QGLContext::fromPlatformGLContext(sharedPlatformGLContext); - if (actualSharedContext == shareContext) { - d->sharing = true;//Will add combination in QGLContext::create - }else { - QGLContextGroup::addShare(this,actualSharedContext); - } - } - } return d->valid; } @@ -325,11 +325,6 @@ void QGLWidget::setMouseTracking(bool enable) bool QGLWidget::event(QEvent *e) { Q_D(QGLWidget); - if (e->type() == QEvent::WinIdChange) { - if (platformWindow()) { - d->glcx = QGLContext::fromPlatformGLContext(platformWindow()->glContext()); - } - } return QWidget::event(e); } @@ -377,6 +372,9 @@ QGLContext::QGLContext(QPlatformGLContext *platformContext) Q_D(QGLContext); d->init(0,QGLFormat::fromPlatformWindowFormat(platformContext->platformWindowFormat())); d->platformContext = platformContext; + d->platformContext->setQGLContextHandle(this,qDeleteQGLContext); + d->valid = true; + d->setupSharing(); } QGLContext *QGLContext::fromPlatformGLContext(QPlatformGLContext *platformContext) @@ -389,11 +387,6 @@ QGLContext *QGLContext::fromPlatformGLContext(QPlatformGLContext *platformContex QGLContext *glContext = new QGLContext(platformContext); //Dont call create on context. This can cause the platformFormat to be set on the widget, which //will cause the platformWindow to be recreated. - glContext->d_func()->platformContext->setQGLContextHandle(glContext,qDeleteQGLContext); - QGLFormat format = QGLFormat::fromPlatformWindowFormat(platformContext->platformWindowFormat()); - glContext->d_func()->glFormat = format; - glContext->d_func()->valid = true; - return glContext; } diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp index 031837f..cda1c79 100644 --- a/src/opengl/qglframebufferobject.cpp +++ b/src/opengl/qglframebufferobject.cpp @@ -1216,8 +1216,8 @@ void QGLFramebufferObject::drawTexture(const QPointF &point, QMacCompatGLuint te } #endif -Q_DECL_IMPORT extern int qt_defaultDpiX(); -Q_DECL_IMPORT extern int qt_defaultDpiY(); +Q_GUI_EXPORT int qt_defaultDpiX(); +Q_GUI_EXPORT int qt_defaultDpiY(); /*! \reimp */ int QGLFramebufferObject::metric(PaintDeviceMetric metric) const diff --git a/src/opengl/qglpaintdevice.cpp b/src/opengl/qglpaintdevice.cpp index ae61ca1..7d0eb5b 100644 --- a/src/opengl/qglpaintdevice.cpp +++ b/src/opengl/qglpaintdevice.cpp @@ -145,8 +145,10 @@ bool QGLPaintDevice::alphaRequested() const return context()->d_func()->reqFormat.alpha(); } - - +bool QGLPaintDevice::isFlipped() const +{ + return false; +} ////////////////// QGLWidgetGLPaintDevice ////////////////// diff --git a/src/opengl/qglpaintdevice_p.h b/src/opengl/qglpaintdevice_p.h index f90edf4..f4176fb 100644 --- a/src/opengl/qglpaintdevice_p.h +++ b/src/opengl/qglpaintdevice_p.h @@ -76,6 +76,7 @@ public: virtual QGLFormat format() const; virtual QSize size() const = 0; virtual bool alphaRequested() const; + virtual bool isFlipped() const; // returns the QGLPaintDevice for the given QPaintDevice static QGLPaintDevice* getDevice(QPaintDevice*); diff --git a/src/opengl/qglpixelbuffer.cpp b/src/opengl/qglpixelbuffer.cpp index 3ce912a..ec6ac4c 100644 --- a/src/opengl/qglpixelbuffer.cpp +++ b/src/opengl/qglpixelbuffer.cpp @@ -424,8 +424,8 @@ QPaintEngine *QGLPixelBuffer::paintEngine() const #endif } -Q_DECL_IMPORT extern int qt_defaultDpiX(); -Q_DECL_IMPORT extern int qt_defaultDpiY(); +Q_GUI_EXPORT int qt_defaultDpiX(); +Q_GUI_EXPORT int qt_defaultDpiY(); /*! \reimp */ int QGLPixelBuffer::metric(PaintDeviceMetric metric) const diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index a3867dd..aa07561 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -63,8 +63,8 @@ QT_BEGIN_NAMESPACE // qpixmapfilter.cpp -Q_DECL_IMPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0); -Q_DECL_IMPORT QImage qt_halfScaled(const QImage &source); +Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0); +Q_GUI_EXPORT QImage qt_halfScaled(const QImage &source); void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const { @@ -425,7 +425,7 @@ static inline uint nextMultiple(uint x, uint multiplier) return x + multiplier - mod; } -Q_DECL_IMPORT void qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride, +Q_GUI_EXPORT void qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride, quint32 *dest, int dstStride); bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp index 7f41339..4598bff 100644 --- a/src/opengl/qglshaderprogram.cpp +++ b/src/opengl/qglshaderprogram.cpp @@ -261,14 +261,14 @@ bool QGLShaderPrivate::compile(QGLShader *q) log = QString::fromLatin1(logbuf); QString name = q->objectName(); - char *types[] = { + const char *types[] = { "Fragment", "Vertex", "Geometry", "" }; - char *type = types[3]; + const char *type = types[3]; if (shaderType == QGLShader::Fragment) type = types[0]; else if (shaderType == QGLShader::Vertex) diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp index 2f1b8fd..2c01ac4 100644 --- a/src/opengl/qpaintengine_opengl.cpp +++ b/src/opengl/qpaintengine_opengl.cpp @@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE -Q_DECL_IMPORT extern QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp +Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); //in qbrush.cpp #ifdef QT_MAC_USE_COCOA extern void *qt_current_nsopengl_context(); // qgl_mac.mm #endif diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index e522386..d12704d 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -739,8 +739,8 @@ QGLTexture* QGLPixmapData::texture() const return &m_texture; } -Q_DECL_IMPORT extern int qt_defaultDpiX(); -Q_DECL_IMPORT extern int qt_defaultDpiY(); +Q_GUI_EXPORT int qt_defaultDpiX(); +Q_GUI_EXPORT int qt_defaultDpiY(); int QGLPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const { diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp index 8b54368..5a783ca 100644 --- a/src/opengl/qwindowsurface_gl.cpp +++ b/src/opengl/qwindowsurface_gl.cpp @@ -402,11 +402,6 @@ void QGLWindowSurface::hijackWindow(QWidget *widget) if (widgetPrivate->extraData()->glContext) return; -#ifdef Q_WS_QPA - QGLContext *ctx = QGLContext::fromPlatformGLContext(widget->platformWindow()->glContext()); - if (!d_ptr->fbo && d_ptr->tried_fbo) - d_ptr->ctx = ctx; -#else QGLContext *ctx = NULL; // For translucent top-level widgets we need alpha in the format. @@ -419,8 +414,7 @@ void QGLWindowSurface::hijackWindow(QWidget *widget) } else ctx = new QGLContext(surfaceFormat, widget); - ctx->create(qt_gl_share_widget()->context()); -#endif + ctx->create(qt_gl_share_context()); #ifndef QT_NO_EGL static bool checkedForNOKSwapRegion = false; @@ -480,6 +474,8 @@ static void drawTexture(const QRectF &rect, GLuint tex_id, const QSize &texSize, void QGLWindowSurface::beginPaint(const QRegion &) { + d_ptr->did_paint = true; + if (!context()) return; @@ -494,8 +490,6 @@ void QGLWindowSurface::beginPaint(const QRegion &) glClearColor(0.0, 0.0, 0.0, 0.0); glClear(clearFlags); } - - d_ptr->did_paint = true; } void QGLWindowSurface::endPaint(const QRegion &rgn) diff --git a/src/openvg/openvg.pro b/src/openvg/openvg.pro index e7ed890..e185949 100644 --- a/src/openvg/openvg.pro +++ b/src/openvg/openvg.pro @@ -38,6 +38,13 @@ symbian { DEFINES += QVG_RECREATE_ON_SIZE_CHANGE QVG_BUFFER_SCROLLING QVG_SCISSOR_CLIP SOURCES += \ qvg_symbian.cpp + + contains(QT_CONFIG, freetype) { + DEFINES += QT_NO_FONTCONFIG + INCLUDEPATH += \ + ../3rdparty/freetype/src \ + ../3rdparty/freetype/include + } } include(../qbase.pri) diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp index 09cbc36..a15f0c3 100644 --- a/src/openvg/qpaintengine_vg.cpp +++ b/src/openvg/qpaintengine_vg.cpp @@ -77,8 +77,8 @@ static const qreal aliasedCoordinateDelta = 0.5 - 0.015625; #if !defined(QVG_NO_DRAW_GLYPHS) -Q_DECL_IMPORT extern int qt_defaultDpiX(); -Q_DECL_IMPORT extern int qt_defaultDpiY(); +Q_GUI_EXPORT int qt_defaultDpiX(); +Q_GUI_EXPORT int qt_defaultDpiY(); class QVGPaintEnginePrivate; @@ -526,7 +526,7 @@ void QVGPaintEnginePrivate::setTransform vgLoadMatrix(mat); } -Q_DECL_IMPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale); +Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale); void QVGPaintEnginePrivate::updateTransform(QPaintDevice *pdev) { @@ -994,7 +994,7 @@ VGPath QVGPaintEnginePrivate::roundedRectPath(const QRectF &rect, qreal xRadius, return vgpath; } -Q_DECL_IMPORT extern QImage qt_imageForBrush(int style, bool invert); +Q_GUI_EXPORT QImage qt_imageForBrush(int style, bool invert); static QImage colorizeBitmap(const QImage &image, const QColor &color) { @@ -1472,7 +1472,7 @@ void QVGPaintEnginePrivate::draw (VGPath path, const QPen& pen, const QBrush& brush, VGint rule) { VGbitfield mode = 0; - if (pen.style() != Qt::NoPen) { + if (qpen_style(pen) != Qt::NoPen && qbrush_style(qpen_brush(pen)) != Qt::NoBrush) { ensurePen(pen); mode |= VG_STROKE_PATH; } diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index 3f768fd..732b484 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -200,6 +200,9 @@ bool QVGPixmapData::fromData(const uchar *buffer, uint len, const char *format, return !isNull(); } +/*! + out-of-place conversion (inPlace == false) will always detach() + */ void QVGPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags flags, bool inPlace) { if (image.size() == QSize(w, h)) @@ -216,11 +219,16 @@ void QVGPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags else format = image.hasAlphaChannel() ? sourceFormat() : QImage::Format_RGB32; - if (inPlace && image.data_ptr()->convertInPlace(format, flags)) + if (inPlace && image.data_ptr()->convertInPlace(format, flags)) { source = image; - else + } else { source = image.convertToFormat(format); + // convertToFormat won't detach the image if format stays the same. + if (image.format() == format) + source.detach(); + } + recreate = true; } @@ -404,8 +412,8 @@ void QVGPixmapData::reclaimImages() destroyImages(); } -Q_DECL_IMPORT extern int qt_defaultDpiX(); -Q_DECL_IMPORT extern int qt_defaultDpiY(); +Q_GUI_EXPORT int qt_defaultDpiX(); +Q_GUI_EXPORT int qt_defaultDpiY(); int QVGPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const { diff --git a/src/plugins/bearer/icd/qnetworksession_impl.cpp b/src/plugins/bearer/icd/qnetworksession_impl.cpp index 6d578d3..af5d85e 100644 --- a/src/plugins/bearer/icd/qnetworksession_impl.cpp +++ b/src/plugins/bearer/icd/qnetworksession_impl.cpp @@ -125,9 +125,6 @@ static QString get_network_interface(); void QNetworkSessionPrivateImpl::iapStateChanged(const QString& iapid, uint icd_connection_state) { - if ((publicConfig.type() == QNetworkConfiguration::UserChoice) && opened) { - updateIdentifier(iapid); - } if (((publicConfig.type() == QNetworkConfiguration::UserChoice) && (activeConfig.identifier() == iapid)) || @@ -149,6 +146,9 @@ void QNetworkSessionPrivateImpl::iapStateChanged(const QString& iapid, uint icd_ break; } } + if (publicConfig.type() == QNetworkConfiguration::UserChoice) { + updateIdentifier(iapid); + } } void QNetworkSessionPrivateImpl::cleanupSession(void) diff --git a/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp b/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp index 20b092e..a70d232 100644 --- a/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp +++ b/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp @@ -42,6 +42,7 @@ #include <QDebug> #include <private/qpixmap_raster_p.h> #include <private/qwindowsurface_gl_p.h> +#include <private/qwindowsurface_raster_p.h> #include <private/qegl_p.h> #include <private/qglextensions_p.h> #include <private/qgl_p.h> @@ -75,7 +76,12 @@ QMeeGoGraphicsSystem::~QMeeGoGraphicsSystem() QWindowSurface* QMeeGoGraphicsSystem::createWindowSurface(QWidget *widget) const { - QGLShareContextScope ctx(qt_gl_share_widget()->context()); + QGLWidget *shareWidget = qt_gl_share_widget(); + + if (!shareWidget) + return new QRasterWindowSurface(widget); + + QGLShareContextScope ctx(shareWidget->context()); QMeeGoGraphicsSystem::surfaceWasCreated = true; QWindowSurface *surface = new QGLWindowSurface(widget); diff --git a/src/plugins/phonon/gstreamer/gstreamer.pro b/src/plugins/phonon/gstreamer/gstreamer.pro index 02e0848..c0d2604 100644 --- a/src/plugins/phonon/gstreamer/gstreamer.pro +++ b/src/plugins/phonon/gstreamer/gstreamer.pro @@ -58,7 +58,7 @@ SOURCES += $$PHONON_GSTREAMER_DIR/abstractrenderer.cpp \ $$PHONON_GSTREAMER_DIR/volumefadereffect.cpp \ $$PHONON_GSTREAMER_DIR/widgetrenderer.cpp -x11 { +!qpa:!embedded{ HEADERS += $$PHONON_GSTREAMER_DIR/x11renderer.h SOURCES += $$PHONON_GSTREAMER_DIR/x11renderer.cpp } diff --git a/src/plugins/platforms/eglconvenience/qeglconvenience.cpp b/src/plugins/platforms/eglconvenience/qeglconvenience.cpp index b203fe8..1612f79 100644 --- a/src/plugins/platforms/eglconvenience/qeglconvenience.cpp +++ b/src/plugins/platforms/eglconvenience/qeglconvenience.cpp @@ -313,4 +313,12 @@ QPlatformWindowFormat qt_qPlatformWindowFormatFromConfig(EGLDisplay display, con return format; } +bool q_hasEglExtension(EGLDisplay display, const char* extensionName) +{ + QList<QByteArray> extensions = + QByteArray(reinterpret_cast<const char *> + (eglQueryString(display, EGL_EXTENSIONS))).split(' '); + return extensions.contains(extensionName); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglconvenience/qeglconvenience.h b/src/plugins/platforms/eglconvenience/qeglconvenience.h index 604262b..98c30b8 100644 --- a/src/plugins/platforms/eglconvenience/qeglconvenience.h +++ b/src/plugins/platforms/eglconvenience/qeglconvenience.h @@ -53,6 +53,7 @@ QVector<EGLint> q_createConfigAttributesFromFormat(const QPlatformWindowFormat & bool q_reduceConfigAttributes(QVector<EGLint> *configAttributes); EGLConfig q_configFromQPlatformWindowFormat(EGLDisplay display, const QPlatformWindowFormat &format); QPlatformWindowFormat qt_qPlatformWindowFormatFromConfig(EGLDisplay display, const EGLConfig config); +bool q_hasEglExtension(EGLDisplay display,const char* extensionName); QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index d0e15d3..b5b7e05 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -62,6 +62,9 @@ void QEglFSWindow::setGeometry(const QRect &) QRect rect(m_screen->availableGeometry()); QWindowSystemInterface::handleGeometryChange(this->widget(), rect); + // Since toplevels are fullscreen, propegate the screen size back to the widget + widget()->setGeometry(rect); + QPlatformWindow::setGeometry(rect); } diff --git a/src/plugins/platforms/externalplugin.pri b/src/plugins/platforms/externalplugin.pri new file mode 100644 index 0000000..54da4d9 --- /dev/null +++ b/src/plugins/platforms/externalplugin.pri @@ -0,0 +1,29 @@ +# +# Lighthouse now has preliminarily support for building and +# loading platform plugins from outside the Qt source/build +# tree. +# +# 1) Building external plugins: +# Set QTDIR to the Qt build directory, copy this file to +# the plugin source repository and include it at the top +# of the plugin's pro file. Use QT_SOURCE_TREE if you +# want to pull in source code from Qt: +# +# include($$QT_SOURCE_TREE/src/plugins/platforms/fontdatabases/genericunix/genericunix.pri) +# +# 2) Loading external plugins: +# Specify the path to the directory containing the +# plugin on the command line, in addition to the +# platform name. +# +# ./wiggly -platformPluginPath /path/to/myPlugin -platform gullfaksA +# + +!exists($$(QTDIR)/.qmake.cache) { + error("Please set QTDIR to the Qt build directory") +} + +QT_SOURCE_TREE = $$fromfile($$(QTDIR)/.qmake.cache,QT_SOURCE_TREE) +QT_BUILD_TREE = $$fromfile($$(QTDIR)/.qmake.cache,QT_BUILD_TREE) + +include($$QT_SOURCE_TREE/src/plugins/qpluginbase.pri) diff --git a/src/plugins/platforms/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/plugins/platforms/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 92f30fc..9b9be07 100644 --- a/src/plugins/platforms/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/plugins/platforms/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -274,6 +274,25 @@ static const char *openType[] = { "nko " // N'Ko }; +static const char *getFcFamilyForStyleHint(const QFont::StyleHint style) +{ + const char *stylehint = 0; + switch (style) { + case QFont::SansSerif: + stylehint = "sans-serif"; + break; + case QFont::Serif: + stylehint = "serif"; + break; + case QFont::TypeWriter: + stylehint = "monospace"; + break; + default: + break; + } + return stylehint; +} + void QFontconfigDatabase::populateFontDatabase() { FcFontSet *fonts; @@ -522,7 +541,7 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, QUnicodeTables:: return engine; } -QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QUnicodeTables::Script &script) const +QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const { QStringList fallbackFamilies; FcPattern *pattern = FcPatternCreate(); @@ -550,6 +569,12 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const FcLangSetDestroy(ls); } + const char *stylehint = getFcFamilyForStyleHint(styleHint); + if (stylehint) { + value.u.s = (const FcChar8 *)stylehint; + FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue); + } + FcConfigSubstitute(0, pattern, FcMatchPattern); FcConfigSubstitute(0, pattern, FcMatchFont); diff --git a/src/plugins/platforms/fontdatabases/fontconfig/qfontconfigdatabase.h b/src/plugins/platforms/fontdatabases/fontconfig/qfontconfigdatabase.h index 33382dc..cf62b88 100644 --- a/src/plugins/platforms/fontdatabases/fontconfig/qfontconfigdatabase.h +++ b/src/plugins/platforms/fontdatabases/fontconfig/qfontconfigdatabase.h @@ -50,7 +50,7 @@ class QFontconfigDatabase : public QBasicUnixFontDatabase public: void populateFontDatabase(); QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); - QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QUnicodeTables::Script &script) const; + QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; }; #endif // QFONTCONFIGDATABASE_H diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index 26ccd44..57015e7 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -2,3 +2,7 @@ TEMPLATE = subdirs SUBDIRS += minimal +contains(QT_CONFIG, wayland) { + SUBDIRS += wayland +} + diff --git a/src/plugins/platforms/testlite/qglxintegration.cpp b/src/plugins/platforms/testlite/qglxintegration.cpp index a4b7b69..8023014 100644 --- a/src/plugins/platforms/testlite/qglxintegration.cpp +++ b/src/plugins/platforms/testlite/qglxintegration.cpp @@ -44,7 +44,9 @@ #include <QGLFormat> #include "qtestlitewindow.h" +#include "qtestlitescreen.h" +#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) #include <X11/Xlib.h> #include <X11/Xutil.h> #include <GL/glx.h> @@ -57,9 +59,9 @@ QT_BEGIN_NAMESPACE -QMutex QGLXGLContext::m_defaultSharedContextMutex(QMutex::Recursive); +QMutex QGLXContext::m_defaultSharedContextMutex(QMutex::Recursive); -QVector<int> QGLXGLContext::buildSpec(const QPlatformWindowFormat &format) +QVector<int> QGLXContext::buildSpec(const QPlatformWindowFormat &format) { QVector<int> spec(48); int i = 0; @@ -111,7 +113,7 @@ QVector<int> QGLXGLContext::buildSpec(const QPlatformWindowFormat &format) return spec; } -GLXFBConfig QGLXGLContext::findConfig(const MyDisplay *xd, const QPlatformWindowFormat &format) +GLXFBConfig QGLXContext::findConfig(const QTestLiteScreen *screen, const QPlatformWindowFormat &format) { bool reduced = true; GLXFBConfig chosenConfig = 0; @@ -120,7 +122,7 @@ GLXFBConfig QGLXGLContext::findConfig(const MyDisplay *xd, const QPlatformWindow QVector<int> spec = buildSpec(reducedFormat); int confcount = 0; GLXFBConfig *configs; - configs = glXChooseFBConfig(xd->display,xd->screen,spec.constData(),&confcount); + configs = glXChooseFBConfig(screen->display(),screen->xScreenNumber(),spec.constData(),&confcount); if (confcount) { for (int i = 0; i < confcount; i++) { @@ -128,7 +130,7 @@ GLXFBConfig QGLXGLContext::findConfig(const MyDisplay *xd, const QPlatformWindow // Make sure we try to get an ARGB visual if the format asked for an alpha: if (reducedFormat.alpha()) { int alphaSize; - glXGetFBConfigAttrib(xd->display,configs[i],GLX_ALPHA_SIZE,&alphaSize); + glXGetFBConfigAttrib(screen->display(),configs[i],GLX_ALPHA_SIZE,&alphaSize); if (alphaSize > 0) break; } else { @@ -147,14 +149,14 @@ GLXFBConfig QGLXGLContext::findConfig(const MyDisplay *xd, const QPlatformWindow return chosenConfig; } -XVisualInfo *QGLXGLContext::findVisualInfo(const MyDisplay *xd, const QPlatformWindowFormat &format) +XVisualInfo *QGLXContext::findVisualInfo(const QTestLiteScreen *screen, const QPlatformWindowFormat &format) { - GLXFBConfig config = QGLXGLContext::findConfig(xd,format); - XVisualInfo *visualInfo = glXGetVisualFromFBConfig(xd->display,config); + GLXFBConfig config = QGLXContext::findConfig(screen,format); + XVisualInfo *visualInfo = glXGetVisualFromFBConfig(screen->display(),config); return visualInfo; } -QPlatformWindowFormat QGLXGLContext::platformWindowFromGLXFBConfig(Display *display, GLXFBConfig config, GLXContext ctx) +QPlatformWindowFormat QGLXContext::platformWindowFromGLXFBConfig(Display *display, GLXFBConfig config, GLXContext ctx) { QPlatformWindowFormat format; int redSize = 0; @@ -210,7 +212,7 @@ QPlatformWindowFormat QGLXGLContext::platformWindowFromGLXFBConfig(Display *disp return format; } -QPlatformWindowFormat QGLXGLContext::reducePlatformWindowFormat(const QPlatformWindowFormat &format, bool *reduced) +QPlatformWindowFormat QGLXContext::reducePlatformWindowFormat(const QPlatformWindowFormat &format, bool *reduced) { QPlatformWindowFormat retFormat = format; *reduced = true; @@ -235,9 +237,9 @@ QPlatformWindowFormat QGLXGLContext::reducePlatformWindowFormat(const QPlatformW return retFormat; } -QGLXGLContext::QGLXGLContext(Window window, MyDisplay *xd, const QPlatformWindowFormat &format) +QGLXContext::QGLXContext(Window window, QTestLiteScreen *screen, const QPlatformWindowFormat &format) : QPlatformGLContext() - , m_xd(xd) + , m_screen(screen) , m_drawable((Drawable)window) , m_context(0) { @@ -246,7 +248,7 @@ QGLXGLContext::QGLXGLContext(Window window, MyDisplay *xd, const QPlatformWindow if (format.useDefaultSharedContext()) { if (!QPlatformGLContext::defaultSharedContext()) { if (m_defaultSharedContextMutex.tryLock()){ - createDefaultSharedContex(xd); + createDefaultSharedContex(screen); m_defaultSharedContextMutex.unlock(); } else { m_defaultSharedContextMutex.lock(); //wait to the the shared context is created @@ -259,32 +261,32 @@ QGLXGLContext::QGLXGLContext(Window window, MyDisplay *xd, const QPlatformWindow } GLXContext shareGlxContext = 0; if (sharePlatformContext) - shareGlxContext = static_cast<const QGLXGLContext*>(sharePlatformContext)->glxContext(); + shareGlxContext = static_cast<const QGLXContext*>(sharePlatformContext)->glxContext(); - GLXFBConfig config = findConfig(xd,format); - m_context = glXCreateNewContext(xd->display,config,GLX_RGBA_TYPE,shareGlxContext,TRUE); - m_windowFormat = QGLXGLContext::platformWindowFromGLXFBConfig(xd->display,config,m_context); + GLXFBConfig config = findConfig(screen,format); + m_context = glXCreateNewContext(screen->display(),config,GLX_RGBA_TYPE,shareGlxContext,TRUE); + m_windowFormat = QGLXContext::platformWindowFromGLXFBConfig(screen->display(),config,m_context); #ifdef MYX11_DEBUG qDebug() << "QGLXGLContext::create context" << m_context; #endif } -QGLXGLContext::QGLXGLContext(MyDisplay *display, Drawable drawable, GLXContext context) - : QPlatformGLContext(), m_xd(display), m_drawable(drawable), m_context(context) +QGLXContext::QGLXContext(QTestLiteScreen *screen, Drawable drawable, GLXContext context) + : QPlatformGLContext(), m_screen(screen), m_drawable(drawable), m_context(context) { } -QGLXGLContext::~QGLXGLContext() +QGLXContext::~QGLXContext() { if (m_context) { qDebug("Destroying GLX context 0x%p", m_context); - glXDestroyContext(m_xd->display, m_context); + glXDestroyContext(m_screen->display(), m_context); } } -void QGLXGLContext::createDefaultSharedContex(MyDisplay *xd) +void QGLXContext::createDefaultSharedContex(QTestLiteScreen *screen) { int x = 0; int y = 0; @@ -293,45 +295,45 @@ void QGLXGLContext::createDefaultSharedContex(MyDisplay *xd) QPlatformWindowFormat format = QPlatformWindowFormat::defaultFormat(); GLXContext context; - GLXFBConfig config = findConfig(xd,format); + GLXFBConfig config = findConfig(screen,format); if (config) { - XVisualInfo *visualInfo = glXGetVisualFromFBConfig(xd->display,config); - Colormap cmap = XCreateColormap(xd->display,xd->rootWindow(),visualInfo->visual,AllocNone); + XVisualInfo *visualInfo = glXGetVisualFromFBConfig(screen->display(),config); + Colormap cmap = XCreateColormap(screen->display(),screen->rootWindow(),visualInfo->visual,AllocNone); XSetWindowAttributes a; a.colormap = cmap; - Window sharedWindow = XCreateWindow(xd->display, xd->rootWindow(),x, y, w, h, + Window sharedWindow = XCreateWindow(screen->display(), screen->rootWindow(),x, y, w, h, 0, visualInfo->depth, InputOutput, visualInfo->visual, CWColormap, &a); - context = glXCreateNewContext(xd->display,config,GLX_RGBA_TYPE,0,TRUE); - QPlatformGLContext *sharedContext = new QGLXGLContext(xd,sharedWindow,context); + context = glXCreateNewContext(screen->display(),config,GLX_RGBA_TYPE,0,TRUE); + QPlatformGLContext *sharedContext = new QGLXContext(screen,sharedWindow,context); QPlatformGLContext::setDefaultSharedContext(sharedContext); } else { qWarning("Warning no shared context created"); } } -void QGLXGLContext::makeCurrent() +void QGLXContext::makeCurrent() { QPlatformGLContext::makeCurrent(); #ifdef MYX11_DEBUG qDebug("QGLXGLContext::makeCurrent(window=0x%x, ctx=0x%x)", m_drawable, m_context); #endif - glXMakeCurrent(m_xd->display, m_drawable, m_context); + glXMakeCurrent(m_screen->display(), m_drawable, m_context); } -void QGLXGLContext::doneCurrent() +void QGLXContext::doneCurrent() { QPlatformGLContext::doneCurrent(); - glXMakeCurrent(m_xd->display, 0, 0); + glXMakeCurrent(m_screen->display(), 0, 0); } -void QGLXGLContext::swapBuffers() +void QGLXContext::swapBuffers() { - glXSwapBuffers(m_xd->display, m_drawable); + glXSwapBuffers(m_screen->display(), m_drawable); } -void* QGLXGLContext::getProcAddress(const QString& procName) +void* QGLXContext::getProcAddress(const QString& procName) { typedef void *(*qt_glXGetProcAddressARB)(const GLubyte *); static qt_glXGetProcAddressARB glXGetProcAddressARB = 0; @@ -340,7 +342,7 @@ void* QGLXGLContext::getProcAddress(const QString& procName) if (resolved && !glXGetProcAddressARB) return 0; if (!glXGetProcAddressARB) { - QList<QByteArray> glxExt = QByteArray(glXGetClientString(m_xd->display, GLX_EXTENSIONS)).split(' '); + QList<QByteArray> glxExt = QByteArray(glXGetClientString(m_screen->display(), GLX_EXTENSIONS)).split(' '); if (glxExt.contains("GLX_ARB_get_proc_address")) { #if defined(Q_OS_LINUX) || defined(Q_OS_BSD4) void *handle = dlopen(NULL, RTLD_LAZY); @@ -364,9 +366,11 @@ void* QGLXGLContext::getProcAddress(const QString& procName) return glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName.toLatin1().data())); } -QPlatformWindowFormat QGLXGLContext::platformWindowFormat() const +QPlatformWindowFormat QGLXContext::platformWindowFormat() const { return m_windowFormat; } QT_END_NAMESPACE + +#endif //!defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) diff --git a/src/plugins/platforms/testlite/qglxintegration.h b/src/plugins/platforms/testlite/qglxintegration.h index e17790e..abece45 100644 --- a/src/plugins/platforms/testlite/qglxintegration.h +++ b/src/plugins/platforms/testlite/qglxintegration.h @@ -49,17 +49,16 @@ #include <QtCore/QMutex> +#if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) #include <GL/glx.h> QT_BEGIN_NAMESPACE -class MyDisplay; - -class QGLXGLContext : public QPlatformGLContext +class QGLXContext : public QPlatformGLContext { public: - QGLXGLContext(Window window, MyDisplay *xd, const QPlatformWindowFormat &format); - ~QGLXGLContext(); + QGLXContext(Window window, QTestLiteScreen *xd, const QPlatformWindowFormat &format); + ~QGLXContext(); virtual void makeCurrent(); virtual void doneCurrent(); @@ -70,24 +69,26 @@ public: QPlatformWindowFormat platformWindowFormat() const; - static XVisualInfo *findVisualInfo(const MyDisplay *xd, const QPlatformWindowFormat &format); + static XVisualInfo *findVisualInfo(const QTestLiteScreen *xd, const QPlatformWindowFormat &format); private: - static GLXFBConfig findConfig(const MyDisplay *xd,const QPlatformWindowFormat &format); + static GLXFBConfig findConfig(const QTestLiteScreen *xd,const QPlatformWindowFormat &format); static QVector<int> buildSpec(const QPlatformWindowFormat &format); static QPlatformWindowFormat platformWindowFromGLXFBConfig(Display *display, GLXFBConfig config, GLXContext context); static QPlatformWindowFormat reducePlatformWindowFormat(const QPlatformWindowFormat &format, bool *reduced); - MyDisplay *m_xd; + QTestLiteScreen *m_screen; Drawable m_drawable; GLXContext m_context; QPlatformWindowFormat m_windowFormat; - QGLXGLContext (MyDisplay *display, Drawable drawable, GLXContext context); + QGLXContext (QTestLiteScreen *screen, Drawable drawable, GLXContext context); static QMutex m_defaultSharedContextMutex; - static void createDefaultSharedContex(MyDisplay *xd); + static void createDefaultSharedContex(QTestLiteScreen *xd); }; QT_END_NAMESPACE +#endif //!defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) + #endif diff --git a/src/plugins/platforms/testlite/qtestliteclipboard.cpp b/src/plugins/platforms/testlite/qtestliteclipboard.cpp new file mode 100644 index 0000000..9bab7f9 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestliteclipboard.cpp @@ -0,0 +1,676 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtestliteclipboard.h" + +#include "qtestlitescreen.h" +#include "qtestlitemime.h" + +#include <private/qapplication_p.h> + +#include <QtCore/QDebug> + +class QTestLiteClipboardMime : public QTestLiteMime +{ + Q_OBJECT +public: + QTestLiteClipboardMime(QClipboard::Mode mode, QTestLiteClipboard *clipboard) + : QTestLiteMime() + , m_clipboard(clipboard) + { + switch (mode) { + case QClipboard::Selection: + modeAtom = XA_PRIMARY; + break; + + case QClipboard::Clipboard: + modeAtom = QTestLiteStatic::atom(QTestLiteStatic::CLIPBOARD); + break; + + default: + qWarning("QTestLiteMime: Internal error: Unsupported clipboard mode"); + break; + } + } + +protected: + QStringList formats_sys() const + { + if (empty()) + return QStringList(); + + if (!formatList.count()) { + QTestLiteClipboardMime *that = const_cast<QTestLiteClipboardMime *>(this); + // get the list of targets from the current clipboard owner - we do this + // once so that multiple calls to this function don't require multiple + // server round trips... + that->format_atoms = m_clipboard->getDataInFormat(modeAtom,QTestLiteStatic::atom(QTestLiteStatic::TARGETS)); + + if (format_atoms.size() > 0) { + Atom *targets = (Atom *) format_atoms.data(); + int size = format_atoms.size() / sizeof(Atom); + + for (int i = 0; i < size; ++i) { + if (targets[i] == 0) + continue; + + QStringList formatsForAtom = mimeFormatsForAtom(m_clipboard->screen()->display(),targets[i]); + for (int j = 0; j < formatsForAtom.size(); ++j) { + if (!formatList.contains(formatsForAtom.at(j))) + that->formatList.append(formatsForAtom.at(j)); + } + } + } + } + + return formatList; + } + + bool hasFormat_sys(const QString &format) const + { + QStringList list = formats(); + return list.contains(format); + } + + QVariant retrieveData_sys(const QString &fmt, QVariant::Type requestedType) const + { + if (fmt.isEmpty() || empty()) + return QByteArray(); + + (void)formats(); // trigger update of format list + + QList<Atom> atoms; + Atom *targets = (Atom *) format_atoms.data(); + int size = format_atoms.size() / sizeof(Atom); + for (int i = 0; i < size; ++i) + atoms.append(targets[i]); + + QByteArray encoding; + Atom fmtatom = mimeAtomForFormat(m_clipboard->screen()->display(),fmt, requestedType, atoms, &encoding); + + if (fmtatom == 0) + return QVariant(); + + return mimeConvertToFormat(m_clipboard->screen()->display(),fmtatom, m_clipboard->getDataInFormat(modeAtom,fmtatom), fmt, requestedType, encoding); + } +private: + bool empty() const + { + Window win = XGetSelectionOwner(m_clipboard->screen()->display(), modeAtom); + + return win == XNone; + } + + + Atom modeAtom; + QTestLiteClipboard *m_clipboard; + QStringList formatList; + QByteArray format_atoms; +}; + +const int QTestLiteClipboard::clipboard_timeout = 5000; + +QTestLiteClipboard::QTestLiteClipboard(QTestLiteScreen *screen) + : QPlatformClipboard() + , m_screen(screen) + , m_xClipboard(0) + , m_clientClipboard(0) + , m_xSelection(0) + , m_clientSelection(0) + , m_requestor(XNone) + , m_owner(XNone) +{ +} + +const QMimeData * QTestLiteClipboard::mimeData(QClipboard::Mode mode) const +{ + if (mode == QClipboard::Clipboard) { + if (!m_xClipboard) { + QTestLiteClipboard *that = const_cast<QTestLiteClipboard *>(this); + that->m_xClipboard = new QTestLiteClipboardMime(mode,that); + } + Window clipboardOwner = XGetSelectionOwner(screen()->display(),QTestLiteStatic::atom(QTestLiteStatic::CLIPBOARD)); + if (clipboardOwner == owner()) { + return m_clientClipboard; + } else { + return m_xClipboard; + } + } else if (mode == QClipboard::Selection) { + if (!m_xSelection) { + QTestLiteClipboard *that = const_cast<QTestLiteClipboard *>(this); + that->m_xSelection = new QTestLiteClipboardMime(mode,that); + } + Window clipboardOwner = XGetSelectionOwner(screen()->display(),XA_PRIMARY); + if (clipboardOwner == owner()) { + return m_clientSelection; + } else { + return m_xSelection; + } + } + return 0; +} + +void QTestLiteClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + Atom modeAtom; + QMimeData **d; + switch (mode) { + case QClipboard::Selection: + modeAtom = XA_PRIMARY; + d = &m_clientSelection; + break; + + case QClipboard::Clipboard: + modeAtom = QTestLiteStatic::atom(QTestLiteStatic::CLIPBOARD); + d = &m_clientClipboard; + break; + + default: + qWarning("QClipboard::setMimeData: unsupported mode '%d'", mode); + return; + } + + Window newOwner; + + if (! data) { // no data, clear clipboard contents + newOwner = XNone; + } else { + newOwner = owner(); + + *d = data; + } + + XSetSelectionOwner(m_screen->display(), modeAtom, newOwner, CurrentTime); + + if (XGetSelectionOwner(m_screen->display(), modeAtom) != newOwner) { + qWarning("QClipboard::setData: Cannot set X11 selection owner"); + } + +} + +bool QTestLiteClipboard::supportsMode(QClipboard::Mode mode) const +{ + if (mode == QClipboard::Clipboard || mode == QClipboard::Selection) + return true; + return false; +} + + +QTestLiteScreen * QTestLiteClipboard::screen() const +{ + return m_screen; +} + +Window QTestLiteClipboard::requestor() const +{ + if (!m_requestor) { + int x = 0, y = 0, w = 3, h = 3; + QTestLiteClipboard *that = const_cast<QTestLiteClipboard *>(this); + Window window = XCreateSimpleWindow(m_screen->display(), m_screen->rootWindow(), + x, y, w, h, 0 /*border_width*/, + m_screen->blackPixel(), m_screen->whitePixel()); + that->setRequestor(window); + } + return m_requestor; +} + +void QTestLiteClipboard::setRequestor(Window window) +{ + if (m_requestor != XNone) { + XDestroyWindow(m_screen->display(),m_requestor); + } + m_requestor = window; +} + +Window QTestLiteClipboard::owner() const +{ + if (!m_owner) { + int x = 0, y = 0, w = 3, h = 3; + QTestLiteClipboard *that = const_cast<QTestLiteClipboard *>(this); + Window window = XCreateSimpleWindow(m_screen->display(), m_screen->rootWindow(), + x, y, w, h, 0 /*border_width*/, + m_screen->blackPixel(), m_screen->whitePixel()); + that->setOwner(window); + } + return m_owner; +} + +void QTestLiteClipboard::setOwner(Window window) +{ + if (m_owner != XNone){ + XDestroyWindow(m_screen->display(),m_owner); + } + m_owner = window; +} + +Atom QTestLiteClipboard::sendTargetsSelection(QMimeData *d, Window window, Atom property) +{ + QVector<Atom> types; + QStringList formats = QInternalMimeData::formatsHelper(d); + for (int i = 0; i < formats.size(); ++i) { + QList<Atom> atoms = QTestLiteMime::mimeAtomsForFormat(screen()->display(),formats.at(i)); + for (int j = 0; j < atoms.size(); ++j) { + if (!types.contains(atoms.at(j))) + types.append(atoms.at(j)); + } + } + types.append(QTestLiteStatic::atom(QTestLiteStatic::TARGETS)); + types.append(QTestLiteStatic::atom(QTestLiteStatic::MULTIPLE)); + types.append(QTestLiteStatic::atom(QTestLiteStatic::TIMESTAMP)); + types.append(QTestLiteStatic::atom(QTestLiteStatic::SAVE_TARGETS)); + + XChangeProperty(screen()->display(), window, property, XA_ATOM, 32, + PropModeReplace, (uchar *) types.data(), types.size()); + return property; +} + +Atom QTestLiteClipboard::sendSelection(QMimeData *d, Atom target, Window window, Atom property) +{ + Atom atomFormat = target; + int dataFormat = 0; + QByteArray data; + + QString fmt = QTestLiteMime::mimeAtomToString(screen()->display(), target); + if (fmt.isEmpty()) { // Not a MIME type we have + qDebug() << "QClipboard: send_selection(): converting to type '%s' is not supported" << fmt.data(); + return XNone; + } + qDebug() << "QClipboard: send_selection(): converting to type '%s'" << fmt.data(); + + if (QTestLiteMime::mimeDataForAtom(screen()->display(),target, d, &data, &atomFormat, &dataFormat)) { + + // don't allow INCR transfers when using MULTIPLE or to + // Motif clients (since Motif doesn't support INCR) + static Atom motif_clip_temporary = QTestLiteStatic::atom(QTestLiteStatic::CLIP_TEMPORARY); + bool allow_incr = property != motif_clip_temporary; + + // X_ChangeProperty protocol request is 24 bytes + const int increment = (XMaxRequestSize(screen()->display()) * 4) - 24; + if (data.size() > increment && allow_incr) { + long bytes = data.size(); + XChangeProperty(screen()->display(), window, property, + QTestLiteStatic::atom(QTestLiteStatic::INCR), 32, PropModeReplace, (uchar *) &bytes, 1); + +// (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment); + qDebug() << "not implemented INCRT just YET!"; + return property; + } + + // make sure we can perform the XChangeProperty in a single request + if (data.size() > increment) + return XNone; // ### perhaps use several XChangeProperty calls w/ PropModeAppend? + int dataSize = data.size() / (dataFormat / 8); + // use a single request to transfer data + XChangeProperty(screen()->display(), window, property, atomFormat, + dataFormat, PropModeReplace, (uchar *) data.data(), + dataSize); + } + return property; +} + +void QTestLiteClipboard::handleSelectionRequest(XEvent *xevent) +{ + XSelectionRequestEvent *req = &xevent->xselectionrequest; + + if (requestor() && req->requestor == requestor()) { + qDebug() << "This should be caught before"; + return; + } + + XEvent event; + event.xselection.type = SelectionNotify; + event.xselection.display = req->display; + event.xselection.requestor = req->requestor; + event.xselection.selection = req->selection; + event.xselection.target = req->target; + event.xselection.property = XNone; + event.xselection.time = req->time; + + QMimeData *d; + if (req->selection == XA_PRIMARY) { + d = m_clientSelection; + } else if (req->selection == QTestLiteStatic::atom(QTestLiteStatic::CLIPBOARD)) { + d = m_clientClipboard; + } else { + qWarning("QClipboard: Unknown selection '%lx'", req->selection); + XSendEvent(screen()->display(), req->requestor, False, NoEventMask, &event); + return; + } + + if (!d) { + qWarning("QClipboard: Cannot transfer data, no data available"); + XSendEvent(screen()->display(), req->requestor, False, NoEventMask, &event); + return; + } + + Atom xa_targets = QTestLiteStatic::atom(QTestLiteStatic::TARGETS); + Atom xa_multiple = QTestLiteStatic::atom(QTestLiteStatic::MULTIPLE); + Atom xa_timestamp = QTestLiteStatic::atom(QTestLiteStatic::TIMESTAMP); + + struct AtomPair { Atom target; Atom property; } *multi = 0; + Atom multi_type = XNone; + int multi_format = 0; + int nmulti = 0; + int imulti = -1; + bool multi_writeback = false; + + if (req->target == xa_multiple) { + QByteArray multi_data; + if (req->property == XNone + || !clipboardReadProperty(req->requestor, req->property, false, &multi_data, + 0, &multi_type, &multi_format) + || multi_format != 32) { + // MULTIPLE property not formatted correctly + XSendEvent(screen()->display(), req->requestor, False, NoEventMask, &event); + return; + } + nmulti = multi_data.size()/sizeof(*multi); + multi = new AtomPair[nmulti]; + memcpy(multi,multi_data.data(),multi_data.size()); + imulti = 0; + } + + for (; imulti < nmulti; ++imulti) { + Atom target; + Atom property; + + if (multi) { + target = multi[imulti].target; + property = multi[imulti].property; + } else { + target = req->target; + property = req->property; + if (property == XNone) // obsolete client + property = target; + } + + Atom ret = XNone; + if (target == XNone || property == XNone) { + ; + } else if (target == xa_timestamp) { +// if (d->timestamp != CurrentTime) { +// XChangeProperty(screen()->display(), req->requestor, property, XA_INTEGER, 32, +// PropModeReplace, CurrentTime, 1); +// ret = property; +// } else { +// qWarning("QClipboard: Invalid data timestamp"); +// } + } else if (target == xa_targets) { + ret = sendTargetsSelection(d, req->requestor, property); + } else { + ret = sendSelection(d, target, req->requestor, property); + } + + if (nmulti > 0) { + if (ret == XNone) { + multi[imulti].property = XNone; + multi_writeback = true; + } + } else { + event.xselection.property = ret; + break; + } + } + + if (nmulti > 0) { + if (multi_writeback) { + // according to ICCCM 2.6.2 says to put None back + // into the original property on the requestor window + XChangeProperty(screen()->display(), req->requestor, req->property, multi_type, 32, + PropModeReplace, (uchar *) multi, nmulti * 2); + } + + delete [] multi; + event.xselection.property = req->property; + } + + // send selection notify to requestor + XSendEvent(screen()->display(), req->requestor, False, NoEventMask, &event); +} + +static inline int maxSelectionIncr(Display *dpy) +{ return XMaxRequestSize(dpy) > 65536 ? 65536*4 : XMaxRequestSize(dpy)*4 - 100; } + +bool QTestLiteClipboard::clipboardReadProperty(Window win, Atom property, bool deleteProperty, QByteArray *buffer, int *size, Atom *type, int *format) const +{ + int maxsize = maxSelectionIncr(screen()->display()); + ulong bytes_left; // bytes_after + ulong length; // nitems + uchar *data; + Atom dummy_type; + int dummy_format; + int r; + + if (!type) // allow null args + type = &dummy_type; + if (!format) + format = &dummy_format; + + // Don't read anything, just get the size of the property data + r = XGetWindowProperty(screen()->display(), win, property, 0, 0, False, + AnyPropertyType, type, format, + &length, &bytes_left, &data); + if (r != Success || (type && *type == XNone)) { + buffer->resize(0); + return false; + } + XFree((char*)data); + + int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left; + + switch (*format) { + case 8: + default: + format_inc = sizeof(char) / 1; + break; + + case 16: + format_inc = sizeof(short) / 2; + proplen *= sizeof(short) / 2; + break; + + case 32: + format_inc = sizeof(long) / 4; + proplen *= sizeof(long) / 4; + break; + } + + int newSize = proplen; + buffer->resize(newSize); + + bool ok = (buffer->size() == newSize); + + if (ok && newSize) { + // could allocate buffer + + while (bytes_left) { + // more to read... + + r = XGetWindowProperty(screen()->display(), win, property, offset, maxsize/4, + False, AnyPropertyType, type, format, + &length, &bytes_left, &data); + if (r != Success || (type && *type == XNone)) + break; + + offset += length / (32 / *format); + length *= format_inc * (*format) / 8; + + // Here we check if we get a buffer overflow and tries to + // recover -- this shouldn't normally happen, but it doesn't + // hurt to be defensive + if ((int)(buffer_offset + length) > buffer->size()) { + length = buffer->size() - buffer_offset; + + // escape loop + bytes_left = 0; + } + + memcpy(buffer->data() + buffer_offset, data, length); + buffer_offset += length; + + XFree((char*)data); + } + + if (*format == 8 && *type == QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT)) { + // convert COMPOUND_TEXT to a multibyte string + XTextProperty textprop; + textprop.encoding = *type; + textprop.format = *format; + textprop.nitems = buffer_offset; + textprop.value = (unsigned char *) buffer->data(); + + char **list_ret = 0; + int count; + if (XmbTextPropertyToTextList(screen()->display(), &textprop, &list_ret, + &count) == Success && count && list_ret) { + offset = buffer_offset = strlen(list_ret[0]); + buffer->resize(offset); + memcpy(buffer->data(), list_ret[0], offset); + } + if (list_ret) XFreeStringList(list_ret); + } + } + + // correct size, not 0-term. + if (size) + *size = buffer_offset; + + if (deleteProperty) + XDeleteProperty(screen()->display(), win, property); + + XFlush(screen()->display()); + + return ok; +} + +QByteArray QTestLiteClipboard::clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm) +{ + XEvent event; + + QByteArray buf; + QByteArray tmp_buf; + bool alloc_error = false; + int length; + int offset = 0; + + if (nbytes > 0) { + // Reserve buffer + zero-terminator (for text data) + // We want to complete the INCR transfer even if we cannot + // allocate more memory + buf.resize(nbytes+1); + alloc_error = buf.size() != nbytes+1; + } + + for (;;) { + XFlush(screen()->display()); + if (!screen()->waitForClipboardEvent(win,PropertyNotify,&event,clipboard_timeout)) + break; + if (event.xproperty.atom != property || + event.xproperty.state != PropertyNewValue) + continue; + if (clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) { + if (length == 0) { // no more data, we're done + if (nullterm) { + buf.resize(offset+1); + buf[offset] = '\0'; + } else { + buf.resize(offset); + } + return buf; + } else if (!alloc_error) { + if (offset+length > (int)buf.size()) { + buf.resize(offset+length+65535); + if (buf.size() != offset+length+65535) { + alloc_error = true; + length = buf.size() - offset; + } + } + memcpy(buf.data()+offset, tmp_buf.constData(), length); + tmp_buf.resize(0); + offset += length; + } + } else { + break; + } + } + + // timed out ... create a new requestor window, otherwise the requestor + // could consider next request to be still part of this timed out request + setRequestor(0); + + return QByteArray(); +} + +QByteArray QTestLiteClipboard::getDataInFormat(Atom modeAtom, Atom fmtatom) +{ + QByteArray buf; + + Window win = requestor(); + + XSelectInput(screen()->display(), win, NoEventMask); // don't listen for any events + + XDeleteProperty(screen()->display(), win, QTestLiteStatic::atom(QTestLiteStatic::_QT_SELECTION)); + XConvertSelection(screen()->display(), modeAtom, fmtatom, QTestLiteStatic::atom(QTestLiteStatic::_QT_SELECTION), win, CurrentTime); + XSync(screen()->display(), false); + + XEvent xevent; + if (!screen()->waitForClipboardEvent(win,SelectionNotify,&xevent,clipboard_timeout) || + xevent.xselection.property == XNone) { + return buf; + } + + Atom type; + XSelectInput(screen()->display(), win, PropertyChangeMask); + + if (clipboardReadProperty(win, QTestLiteStatic::atom(QTestLiteStatic::_QT_SELECTION), true, &buf, 0, &type, 0)) { + if (type == QTestLiteStatic::atom(QTestLiteStatic::INCR)) { + int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0; + buf = clipboardReadIncrementalProperty(win, QTestLiteStatic::atom(QTestLiteStatic::_QT_SELECTION), nbytes, false); + } + } + + XSelectInput(screen()->display(), win, NoEventMask); + + + return buf; +} + +#include "qtestliteclipboard.moc" diff --git a/src/plugins/platforms/testlite/qtestliteclipboard.h b/src/plugins/platforms/testlite/qtestliteclipboard.h new file mode 100644 index 0000000..76065a2 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestliteclipboard.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTLITECLIPBOARD_H +#define QTESTLITECLIPBOARD_H + +#include <QPlatformClipboard> +#include "qtestlitestaticinfo.h" + +class QTestLiteScreen; +class QTestLiteClipboard : public QPlatformClipboard +{ +public: + QTestLiteClipboard(QTestLiteScreen *screen); + + const QMimeData *mimeData(QClipboard::Mode mode) const; + void setMimeData(QMimeData *data, QClipboard::Mode mode); + + bool supportsMode(QClipboard::Mode mode) const; + + QTestLiteScreen *screen() const; + + Window requestor() const; + void setRequestor(Window window); + + Window owner() const; + + void handleSelectionRequest(XEvent *event); + + bool clipboardReadProperty(Window win, Atom property, bool deleteProperty, QByteArray *buffer, int *size, Atom *type, int *format) const; + QByteArray clipboardReadIncrementalProperty(Window win, Atom property, int nbytes, bool nullterm); + + QByteArray getDataInFormat(Atom modeAtom, Atom fmtatom); + +private: + void setOwner(Window window); + + Atom sendTargetsSelection(QMimeData *d, Window window, Atom property); + Atom sendSelection(QMimeData *d, Atom target, Window window, Atom property); + + QTestLiteScreen *m_screen; + + QMimeData *m_xClipboard; + QMimeData *m_clientClipboard; + + QMimeData *m_xSelection; + QMimeData *m_clientSelection; + + Window m_requestor; + Window m_owner; + + static const int clipboard_timeout; + +}; + +#endif // QTESTLITECLIPBOARD_H diff --git a/src/plugins/platforms/testlite/qtestlitecursor.cpp b/src/plugins/platforms/testlite/qtestlitecursor.cpp new file mode 100644 index 0000000..e7ef673 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitecursor.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtestlitecursor.h" + +#include "qtestliteintegration.h" +#include "qtestlitescreen.h" +#include "qtestlitewindow.h" + +#include <QtGui/QBitmap> + +#include <X11/cursorfont.h> + +QT_BEGIN_NAMESPACE + +QTestLiteCursor::QTestLiteCursor(QTestLiteScreen *screen) + : QPlatformCursor(screen) +{ +} + +void QTestLiteCursor::changeCursor(QCursor *cursor, QWidget *widget) +{ + QTestLiteWindow *w = 0; + if (widget) { + QWidget *window = widget->window(); + w = static_cast<QTestLiteWindow*>(window->platformWindow()); + } else { + // No X11 cursor control when there is no widget under the cursor + return; + } + + if (!w) + return; + + int id = cursor->handle(); + + Cursor c; + if (!cursorMap.contains(id)) { + if (cursor->shape() == Qt::BitmapCursor) + c = createCursorBitmap(cursor); + else + c = createCursorShape(cursor->shape()); + if (!c) { + return; + } + cursorMap.insert(id, c); + } else { + c = cursorMap.value(id); + } + + w->setCursor(c); +} + +Cursor QTestLiteCursor::createCursorBitmap(QCursor * cursor) +{ + XColor bg, fg; + bg.red = 255 << 8; + bg.green = 255 << 8; + bg.blue = 255 << 8; + fg.red = 0; + fg.green = 0; + fg.blue = 0; + QPoint spot = cursor->hotSpot(); + Window rootwin = testLiteScreen()->rootWindow(); + + QImage mapImage = cursor->bitmap()->toImage().convertToFormat(QImage::Format_MonoLSB); + QImage maskImage = cursor->mask()->toImage().convertToFormat(QImage::Format_MonoLSB); + + int width = cursor->bitmap()->width(); + int height = cursor->bitmap()->height(); + int bytesPerLine = mapImage.bytesPerLine(); + int destLineSize = width / 8; + if (width % 8) + destLineSize++; + + const uchar * map = mapImage.bits(); + const uchar * mask = maskImage.bits(); + + char * mapBits = new char[height * destLineSize]; + char * maskBits = new char[height * destLineSize]; + for (int i = 0; i < height; i++) { + memcpy(mapBits + (destLineSize * i),map + (bytesPerLine * i), destLineSize); + memcpy(maskBits + (destLineSize * i),mask + (bytesPerLine * i), destLineSize); + } + + Pixmap cp = XCreateBitmapFromData(testLiteScreen()->display(), rootwin, mapBits, width, height); + Pixmap mp = XCreateBitmapFromData(testLiteScreen()->display(), rootwin, maskBits, width, height); + Cursor c = XCreatePixmapCursor(testLiteScreen()->display(), cp, mp, &fg, &bg, spot.x(), spot.y()); + XFreePixmap(testLiteScreen()->display(), cp); + XFreePixmap(testLiteScreen()->display(), mp); + delete[] mapBits; + delete[] maskBits; + + return c; +} + +Cursor QTestLiteCursor::createCursorShape(int cshape) +{ + Cursor cursor = 0; + + if (cshape < 0 || cshape > Qt::LastCursor) + return 0; + + switch (cshape) { + case Qt::ArrowCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_left_ptr); + break; + case Qt::UpArrowCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_center_ptr); + break; + case Qt::CrossCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_crosshair); + break; + case Qt::WaitCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_watch); + break; + case Qt::IBeamCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_xterm); + break; + case Qt::SizeAllCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_fleur); + break; + case Qt::PointingHandCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_hand2); + break; + case Qt::SizeBDiagCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_top_right_corner); + break; + case Qt::SizeFDiagCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_bottom_right_corner); + break; + case Qt::SizeVerCursor: + case Qt::SplitVCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_sb_v_double_arrow); + break; + case Qt::SizeHorCursor: + case Qt::SplitHCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_sb_h_double_arrow); + break; + case Qt::WhatsThisCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_question_arrow); + break; + case Qt::ForbiddenCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_circle); + break; + case Qt::BusyCursor: + cursor = XCreateFontCursor(testLiteScreen()->display(), XC_watch); + break; + + default: //default cursor for all the rest + break; + } + return cursor; +} + +QTestLiteScreen * QTestLiteCursor::testLiteScreen() const +{ + return static_cast<QTestLiteScreen *>(screen); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/testlite/qtestlitecursor.h b/src/plugins/platforms/testlite/qtestlitecursor.h new file mode 100644 index 0000000..bb3549e --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitecursor.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTLITECURSOR_H +#define QTESTLITECURSOR_H + +#include <QtGui/QPlatformCursor> + +#include "qtestliteintegration.h" + +QT_BEGIN_NAMESPACE + +class QTestLiteCursor : QPlatformCursor +{ +public: + QTestLiteCursor(QTestLiteScreen *screen); + + void changeCursor(QCursor * cursor, QWidget * widget); +private: + + Cursor createCursorBitmap(QCursor * cursor); + Cursor createCursorShape(int cshape); + + QTestLiteScreen *testLiteScreen() const; + QMap<int, Cursor> cursorMap; +}; + +QT_END_NAMESPACE + +#endif // QTESTLITECURSOR_H diff --git a/src/plugins/platforms/testlite/qtestliteeglintegration.cpp b/src/plugins/platforms/testlite/qtestliteeglintegration.cpp new file mode 100644 index 0000000..532c63d --- /dev/null +++ b/src/plugins/platforms/testlite/qtestliteeglintegration.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtestliteeglintegration.h" + +static int countBits(unsigned long mask) +{ + int count = 0; + while (mask != 0) { + if (mask & 1) + ++count; + mask >>= 1; + } + return count; +} + +VisualID QTestLiteEglIntegration::getCompatibleVisualId(Display *display, EGLConfig config) +{ + VisualID visualId = 0; + EGLint eglValue = 0; + + EGLDisplay eglDisplay = eglGetDisplay(display); + + EGLint configRedSize = 0; + eglGetConfigAttrib(eglDisplay, config, EGL_RED_SIZE, &configRedSize); + + EGLint configGreenSize = 0; + eglGetConfigAttrib(eglDisplay, config, EGL_GREEN_SIZE, &configGreenSize); + + EGLint configBlueSize = 0; + eglGetConfigAttrib(eglDisplay, config, EGL_BLUE_SIZE, &configBlueSize); + + EGLint configAlphaSize = 0; + eglGetConfigAttrib(eglDisplay, config, EGL_ALPHA_SIZE, &configAlphaSize); + + eglGetConfigAttrib(eglDisplay, config, EGL_CONFIG_ID, &eglValue); + int configId = eglValue; + + // See if EGL provided a valid VisualID: + eglGetConfigAttrib(eglDisplay, config, EGL_NATIVE_VISUAL_ID, &eglValue); + visualId = (VisualID)eglValue; + if (visualId) { + // EGL has suggested a visual id, so get the rest of the visual info for that id: + XVisualInfo visualInfoTemplate; + memset(&visualInfoTemplate, 0, sizeof(XVisualInfo)); + visualInfoTemplate.visualid = visualId; + + XVisualInfo *chosenVisualInfo; + int matchingCount = 0; + chosenVisualInfo = XGetVisualInfo(display, VisualIDMask, &visualInfoTemplate, &matchingCount); + if (chosenVisualInfo) { + // Skip size checks if implementation supports non-matching visual + // and config (http://bugreports.qt.nokia.com/browse/QTBUG-9444). + if (q_hasEglExtension(eglDisplay,"EGL_NV_post_convert_rounding")) { + XFree(chosenVisualInfo); + return visualId; + } + + int visualRedSize = countBits(chosenVisualInfo->red_mask); + int visualGreenSize = countBits(chosenVisualInfo->green_mask); + int visualBlueSize = countBits(chosenVisualInfo->blue_mask); + int visualAlphaSize = -1; // Need XRender to tell us the alpha channel size + + bool visualMatchesConfig = false; + if ( visualRedSize == configRedSize && + visualGreenSize == configGreenSize && + visualBlueSize == configBlueSize ) + { + // We need XRender to check the alpha channel size of the visual. If we don't have + // the alpha size, we don't check it against the EGL config's alpha size. + if (visualAlphaSize >= 0) + visualMatchesConfig = visualAlphaSize == configAlphaSize; + else + visualMatchesConfig = true; + } + + if (!visualMatchesConfig) { + if (visualAlphaSize >= 0) { + qWarning("Warning: EGL suggested using X Visual ID %d (ARGB%d%d%d%d) for EGL config %d (ARGB%d%d%d%d), but this is incompatable", + (int)visualId, visualAlphaSize, visualRedSize, visualGreenSize, visualBlueSize, + configId, configAlphaSize, configRedSize, configGreenSize, configBlueSize); + } else { + qWarning("Warning: EGL suggested using X Visual ID %d (RGB%d%d%d) for EGL config %d (RGB%d%d%d), but this is incompatable", + (int)visualId, visualRedSize, visualGreenSize, visualBlueSize, + configId, configRedSize, configGreenSize, configBlueSize); + } + visualId = 0; + } + } else { + qWarning("Warning: EGL suggested using X Visual ID %d for EGL config %d, but that isn't a valid ID", + (int)visualId, configId); + visualId = 0; + } + XFree(chosenVisualInfo); + } +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + else + qDebug("EGL did not suggest a VisualID (EGL_NATIVE_VISUAL_ID was zero) for EGLConfig %d", configId); +#endif + + if (visualId) { +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + if (configAlphaSize > 0) + qDebug("Using ARGB Visual ID %d provided by EGL for config %d", (int)visualId, configId); + else + qDebug("Using Opaque Visual ID %d provided by EGL for config %d", (int)visualId, configId); +#endif + return visualId; + } + + // Finally, try to + // use XGetVisualInfo and only use the bit depths to match on: + if (!visualId) { + XVisualInfo visualInfoTemplate; + memset(&visualInfoTemplate, 0, sizeof(XVisualInfo)); + XVisualInfo *matchingVisuals; + int matchingCount = 0; + + visualInfoTemplate.depth = configRedSize + configGreenSize + configBlueSize + configAlphaSize; + matchingVisuals = XGetVisualInfo(display, + VisualDepthMask, + &visualInfoTemplate, + &matchingCount); + if (!matchingVisuals) { + // Try again without taking the alpha channel into account: + visualInfoTemplate.depth = configRedSize + configGreenSize + configBlueSize; + matchingVisuals = XGetVisualInfo(display, + VisualDepthMask, + &visualInfoTemplate, + &matchingCount); + } + + if (matchingVisuals) { + visualId = matchingVisuals[0].visualid; + XFree(matchingVisuals); + } + } + + if (visualId) { +#ifdef QT_DEBUG_X11_VISUAL_SELECTION + qDebug("Using Visual ID %d provided by XGetVisualInfo for EGL config %d", (int)visualId, configId); +#endif + return visualId; + } + + qWarning("Unable to find an X11 visual which matches EGL config %d", configId); + return (VisualID)0; +} diff --git a/src/plugins/platforms/testlite/qtestliteeglintegration.h b/src/plugins/platforms/testlite/qtestliteeglintegration.h new file mode 100644 index 0000000..99e9018 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestliteeglintegration.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTLITEEGLINTEGRATION_H +#define QTESTLITEEGLINTEGRATION_H + +#include "qtestlitestaticinfo.h" +#include "../eglconvenience/qeglconvenience.h" + +class QTestLiteEglIntegration +{ +public: + static VisualID getCompatibleVisualId(Display *display, EGLConfig config); +}; + +#endif // QTESTLITEEGLINTEGRATION_H diff --git a/src/plugins/platforms/testlite/qtestliteintegration.cpp b/src/plugins/platforms/testlite/qtestliteintegration.cpp index 68e9051..9b641d1 100644 --- a/src/plugins/platforms/testlite/qtestliteintegration.cpp +++ b/src/plugins/platforms/testlite/qtestliteintegration.cpp @@ -39,70 +39,35 @@ ** ****************************************************************************/ - - #include "qtestliteintegration.h" #include "qtestlitewindowsurface.h" #include <QtGui/private/qpixmap_raster_p.h> #include <QtCore/qdebug.h> -#include <QPlatformCursor> - #include "qtestlitewindow.h" #include "qgenericunixfontdatabase.h" +#include "qtestlitescreen.h" +#include "qtestliteclipboard.h" -#ifndef QT_NO_OPENGL +#if !defined(QT_NO_OPENGL) +#if !defined(QT_OPENGL_ES_2) #include <GL/glx.h> +#else +#include <EGL/egl.h> +#endif //!defined(QT_OPENGL_ES_2) #include <private/qwindowsurface_gl_p.h> #include <private/qpixmapdata_gl_p.h> #endif //QT_NO_OPENGL QT_BEGIN_NAMESPACE -class MyCursor : QPlatformCursor -{ -public: - MyCursor(QPlatformScreen *screen) : QPlatformCursor(screen) {} - - void changeCursor(QCursor * cursor, QWidget * widget) { - QTestLiteWindow *w = 0; - if (widget) { - QWidget *window = widget->window(); - w = static_cast<QTestLiteWindow*>(window->platformWindow()); - } else { - // No X11 cursor control when there is no widget under the cursor - return; - } - - //qDebug() << "changeCursor" << widget << ws; - if (!w) - return; - - w->setCursor(cursor); - } -}; - - QTestLiteIntegration::QTestLiteIntegration(bool useOpenGL) : mUseOpenGL(useOpenGL) , mFontDb(new QGenericUnixFontDatabase()) + , mClipboard(0) { - xd = new MyDisplay; - mPrimaryScreen = new QTestLiteScreen(); - - mPrimaryScreen->mGeometry = QRect - (0, 0, xd->width, xd->height); - mPrimaryScreen->mDepth = 32; - mPrimaryScreen->mFormat = QImage::Format_RGB32; - mPrimaryScreen->mPhysicalSize = - QSize(xd->physicalWidth, xd->physicalHeight); - mScreens.append(mPrimaryScreen); - - - (void)new MyCursor(mPrimaryScreen); - } QPixmapData *QTestLiteIntegration::createPixmapData(QPixmapData::PixelType type) const @@ -120,21 +85,33 @@ QWindowSurface *QTestLiteIntegration::createWindowSurface(QWidget *widget, WId) if (mUseOpenGL) return new QGLWindowSurface(widget); #endif - return new QTestLiteWindowSurface(mPrimaryScreen, widget); + return new QTestLiteWindowSurface(widget); } QPlatformWindow *QTestLiteIntegration::createPlatformWindow(QWidget *widget, WId /*winId*/) const { - return new QTestLiteWindow(this, mPrimaryScreen, widget); + return new QTestLiteWindow(widget); } QPixmap QTestLiteIntegration::grabWindow(WId window, int x, int y, int width, int height) const { - QImage img = xd->grabWindow(window, x, y, width, height); - return QPixmap::fromImage(img); + QImage image; + QWidget *widget = QWidget::find(window); + if (widget) { + QTestLiteScreen *screen = QTestLiteScreen::testLiteScreenForWidget(widget); + image = screen->grabWindow(window,x,y,width,height); + } else { + for (int i = 0; i < mScreens.size(); i++) { + QTestLiteScreen *screen = static_cast<QTestLiteScreen *>(mScreens[i]); + if (screen->rootWindow() == window) { + image = screen->grabWindow(window,x,y,width,height); + } + } + } + return QPixmap::fromImage(image); } QPlatformFontDatabase *QTestLiteIntegration::fontDatabase() const @@ -142,12 +119,38 @@ QPlatformFontDatabase *QTestLiteIntegration::fontDatabase() const return mFontDb; } +QPlatformClipboard * QTestLiteIntegration::clipboard() const +{ + //Use lazy init since clipboard needs QTestliteScreen + if (!mClipboard) { + QTestLiteIntegration *that = const_cast<QTestLiteIntegration *>(this); + that->mClipboard = new QTestLiteClipboard(mPrimaryScreen); + } + return mClipboard; +} + bool QTestLiteIntegration::hasOpenGL() const { -#ifndef QT_NO_OPENGL - return glXQueryExtension(xd->display, 0, 0) != 0; +#if !defined(QT_NO_OPENGL) +#if !defined(QT_OPENGL_ES_2) + QTestLiteScreen *screen = static_cast<const QTestLiteScreen *>(mScreens.at(0)); + return glXQueryExtension(screen->display(), 0, 0) != 0; +#else + static bool eglHasbeenInitialized = false; + static bool wasEglInitialized = false; + if (!eglHasbeenInitialized) { + eglHasbeenInitialized = true; + QTestLiteScreen *screen = static_cast<const QTestLiteScreen *>(mScreens.at(0)); + EGLint major, minor; + eglBindAPI(EGL_OPENGL_ES_API); + EGLDisplay disp = eglGetDisplay(screen->display()); + wasEglInitialized = eglInitialize(disp,&major,&minor); + } + return wasEglInitialized; +#endif #endif return false; } + QT_END_NAMESPACE diff --git a/src/plugins/platforms/testlite/qtestliteintegration.h b/src/plugins/platforms/testlite/qtestliteintegration.h index 8286ef0..320cf00 100644 --- a/src/plugins/platforms/testlite/qtestliteintegration.h +++ b/src/plugins/platforms/testlite/qtestliteintegration.h @@ -42,34 +42,17 @@ #ifndef QGRAPHICSSYSTEM_TESTLITE_H #define QGRAPHICSSYSTEM_TESTLITE_H -#include <QtGui/QPlatformIntegration> -#include <QtGui/QPlatformScreen> - //make sure textstream is included before any X11 headers #include <QtCore/QTextStream> -QT_BEGIN_NAMESPACE +#include <QtGui/QPlatformIntegration> +#include <QtGui/QPlatformScreen> -class MyDisplay; +#include "qtestlitestaticinfo.h" -class QTestLiteScreen : public QPlatformScreen -{ -public: - QTestLiteScreen() - : mDepth(16), mFormat(QImage::Format_RGB16) {} - ~QTestLiteScreen() {} - - QRect geometry() const { return mGeometry; } - int depth() const { return mDepth; } - QImage::Format format() const { return mFormat; } - QSize physicalSize() const { return mPhysicalSize; } +QT_BEGIN_NAMESPACE -public: - QRect mGeometry; - int mDepth; - QImage::Format mFormat; - QSize mPhysicalSize; -}; +class QTestLiteScreen; class QTestLiteIntegration : public QPlatformIntegration { @@ -85,16 +68,16 @@ public: QList<QPlatformScreen *> screens() const { return mScreens; } QPlatformFontDatabase *fontDatabase() const; + QPlatformClipboard *clipboard() const; bool hasOpenGL() const; - MyDisplay *xd; - private: bool mUseOpenGL; QTestLiteScreen *mPrimaryScreen; QList<QPlatformScreen *> mScreens; QPlatformFontDatabase *mFontDb; + QPlatformClipboard *mClipboard; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/testlite/qtestlitekeyboard.cpp b/src/plugins/platforms/testlite/qtestlitekeyboard.cpp new file mode 100644 index 0000000..5b4ebd7 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitekeyboard.cpp @@ -0,0 +1,1000 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtestlitekeyboard.h" + +#include "qtestlitescreen.h" + +#include <QtGui/QWindowSystemInterface> + +#include <QtCore/QTextCodec> + +#ifndef XK_ISO_Left_Tab +#define XK_ISO_Left_Tab 0xFE20 +#endif + +#ifndef XK_dead_hook +#define XK_dead_hook 0xFE61 +#endif + +#ifndef XK_dead_horn +#define XK_dead_horn 0xFE62 +#endif + +#ifndef XK_Codeinput +#define XK_Codeinput 0xFF37 +#endif + +#ifndef XK_Kanji_Bangou +#define XK_Kanji_Bangou 0xFF37 /* same as codeinput */ +#endif + +// Fix old X libraries +#ifndef XK_KP_Home +#define XK_KP_Home 0xFF95 +#endif +#ifndef XK_KP_Left +#define XK_KP_Left 0xFF96 +#endif +#ifndef XK_KP_Up +#define XK_KP_Up 0xFF97 +#endif +#ifndef XK_KP_Right +#define XK_KP_Right 0xFF98 +#endif +#ifndef XK_KP_Down +#define XK_KP_Down 0xFF99 +#endif +#ifndef XK_KP_Prior +#define XK_KP_Prior 0xFF9A +#endif +#ifndef XK_KP_Next +#define XK_KP_Next 0xFF9B +#endif +#ifndef XK_KP_End +#define XK_KP_End 0xFF9C +#endif +#ifndef XK_KP_Insert +#define XK_KP_Insert 0xFF9E +#endif +#ifndef XK_KP_Delete +#define XK_KP_Delete 0xFF9F +#endif + +// the next lines are taken on 10/2009 from X.org (X11/XF86keysym.h), defining some special +// multimedia keys. They are included here as not every system has them. +#define XF86XK_MonBrightnessUp 0x1008FF02 +#define XF86XK_MonBrightnessDown 0x1008FF03 +#define XF86XK_KbdLightOnOff 0x1008FF04 +#define XF86XK_KbdBrightnessUp 0x1008FF05 +#define XF86XK_KbdBrightnessDown 0x1008FF06 +#define XF86XK_Standby 0x1008FF10 +#define XF86XK_AudioLowerVolume 0x1008FF11 +#define XF86XK_AudioMute 0x1008FF12 +#define XF86XK_AudioRaiseVolume 0x1008FF13 +#define XF86XK_AudioPlay 0x1008FF14 +#define XF86XK_AudioStop 0x1008FF15 +#define XF86XK_AudioPrev 0x1008FF16 +#define XF86XK_AudioNext 0x1008FF17 +#define XF86XK_HomePage 0x1008FF18 +#define XF86XK_Mail 0x1008FF19 +#define XF86XK_Start 0x1008FF1A +#define XF86XK_Search 0x1008FF1B +#define XF86XK_AudioRecord 0x1008FF1C +#define XF86XK_Calculator 0x1008FF1D +#define XF86XK_Memo 0x1008FF1E +#define XF86XK_ToDoList 0x1008FF1F +#define XF86XK_Calendar 0x1008FF20 +#define XF86XK_PowerDown 0x1008FF21 +#define XF86XK_ContrastAdjust 0x1008FF22 +#define XF86XK_Back 0x1008FF26 +#define XF86XK_Forward 0x1008FF27 +#define XF86XK_Stop 0x1008FF28 +#define XF86XK_Refresh 0x1008FF29 +#define XF86XK_PowerOff 0x1008FF2A +#define XF86XK_WakeUp 0x1008FF2B +#define XF86XK_Eject 0x1008FF2C +#define XF86XK_ScreenSaver 0x1008FF2D +#define XF86XK_WWW 0x1008FF2E +#define XF86XK_Sleep 0x1008FF2F +#define XF86XK_Favorites 0x1008FF30 +#define XF86XK_AudioPause 0x1008FF31 +#define XF86XK_AudioMedia 0x1008FF32 +#define XF86XK_MyComputer 0x1008FF33 +#define XF86XK_LightBulb 0x1008FF35 +#define XF86XK_Shop 0x1008FF36 +#define XF86XK_History 0x1008FF37 +#define XF86XK_OpenURL 0x1008FF38 +#define XF86XK_AddFavorite 0x1008FF39 +#define XF86XK_HotLinks 0x1008FF3A +#define XF86XK_BrightnessAdjust 0x1008FF3B +#define XF86XK_Finance 0x1008FF3C +#define XF86XK_Community 0x1008FF3D +#define XF86XK_AudioRewind 0x1008FF3E +#define XF86XK_BackForward 0x1008FF3F +#define XF86XK_Launch0 0x1008FF40 +#define XF86XK_Launch1 0x1008FF41 +#define XF86XK_Launch2 0x1008FF42 +#define XF86XK_Launch3 0x1008FF43 +#define XF86XK_Launch4 0x1008FF44 +#define XF86XK_Launch5 0x1008FF45 +#define XF86XK_Launch6 0x1008FF46 +#define XF86XK_Launch7 0x1008FF47 +#define XF86XK_Launch8 0x1008FF48 +#define XF86XK_Launch9 0x1008FF49 +#define XF86XK_LaunchA 0x1008FF4A +#define XF86XK_LaunchB 0x1008FF4B +#define XF86XK_LaunchC 0x1008FF4C +#define XF86XK_LaunchD 0x1008FF4D +#define XF86XK_LaunchE 0x1008FF4E +#define XF86XK_LaunchF 0x1008FF4F +#define XF86XK_ApplicationLeft 0x1008FF50 +#define XF86XK_ApplicationRight 0x1008FF51 +#define XF86XK_Book 0x1008FF52 +#define XF86XK_CD 0x1008FF53 +#define XF86XK_Calculater 0x1008FF54 +#define XF86XK_Clear 0x1008FF55 +#define XF86XK_ClearGrab 0x1008FE21 +#define XF86XK_Close 0x1008FF56 +#define XF86XK_Copy 0x1008FF57 +#define XF86XK_Cut 0x1008FF58 +#define XF86XK_Display 0x1008FF59 +#define XF86XK_DOS 0x1008FF5A +#define XF86XK_Documents 0x1008FF5B +#define XF86XK_Excel 0x1008FF5C +#define XF86XK_Explorer 0x1008FF5D +#define XF86XK_Game 0x1008FF5E +#define XF86XK_Go 0x1008FF5F +#define XF86XK_iTouch 0x1008FF60 +#define XF86XK_LogOff 0x1008FF61 +#define XF86XK_Market 0x1008FF62 +#define XF86XK_Meeting 0x1008FF63 +#define XF86XK_MenuKB 0x1008FF65 +#define XF86XK_MenuPB 0x1008FF66 +#define XF86XK_MySites 0x1008FF67 +#define XF86XK_News 0x1008FF69 +#define XF86XK_OfficeHome 0x1008FF6A +#define XF86XK_Option 0x1008FF6C +#define XF86XK_Paste 0x1008FF6D +#define XF86XK_Phone 0x1008FF6E +#define XF86XK_Reply 0x1008FF72 +#define XF86XK_Reload 0x1008FF73 +#define XF86XK_RotateWindows 0x1008FF74 +#define XF86XK_RotationPB 0x1008FF75 +#define XF86XK_RotationKB 0x1008FF76 +#define XF86XK_Save 0x1008FF77 +#define XF86XK_Send 0x1008FF7B +#define XF86XK_Spell 0x1008FF7C +#define XF86XK_SplitScreen 0x1008FF7D +#define XF86XK_Support 0x1008FF7E +#define XF86XK_TaskPane 0x1008FF7F +#define XF86XK_Terminal 0x1008FF80 +#define XF86XK_Tools 0x1008FF81 +#define XF86XK_Travel 0x1008FF82 +#define XF86XK_Video 0x1008FF87 +#define XF86XK_Word 0x1008FF89 +#define XF86XK_Xfer 0x1008FF8A +#define XF86XK_ZoomIn 0x1008FF8B +#define XF86XK_ZoomOut 0x1008FF8C +#define XF86XK_Away 0x1008FF8D +#define XF86XK_Messenger 0x1008FF8E +#define XF86XK_WebCam 0x1008FF8F +#define XF86XK_MailForward 0x1008FF90 +#define XF86XK_Pictures 0x1008FF91 +#define XF86XK_Music 0x1008FF92 +#define XF86XK_Battery 0x1008FF93 +#define XF86XK_Bluetooth 0x1008FF94 +#define XF86XK_WLAN 0x1008FF95 +#define XF86XK_UWB 0x1008FF96 +#define XF86XK_AudioForward 0x1008FF97 +#define XF86XK_AudioRepeat 0x1008FF98 +#define XF86XK_AudioRandomPlay 0x1008FF99 +#define XF86XK_Subtitle 0x1008FF9A +#define XF86XK_AudioCycleTrack 0x1008FF9B +#define XF86XK_Time 0x1008FF9F +#define XF86XK_Select 0x1008FFA0 +#define XF86XK_View 0x1008FFA1 +#define XF86XK_TopMenu 0x1008FFA2 +#define XF86XK_Suspend 0x1008FFA7 +#define XF86XK_Hibernate 0x1008FFA8 + + +// end of XF86keysyms.h + +// Special keys used by Qtopia, mapped into the X11 private keypad range. +#define QTOPIAXK_Select 0x11000601 +#define QTOPIAXK_Yes 0x11000602 +#define QTOPIAXK_No 0x11000603 +#define QTOPIAXK_Cancel 0x11000604 +#define QTOPIAXK_Printer 0x11000605 +#define QTOPIAXK_Execute 0x11000606 +#define QTOPIAXK_Sleep 0x11000607 +#define QTOPIAXK_Play 0x11000608 +#define QTOPIAXK_Zoom 0x11000609 +#define QTOPIAXK_Context1 0x1100060A +#define QTOPIAXK_Context2 0x1100060B +#define QTOPIAXK_Context3 0x1100060C +#define QTOPIAXK_Context4 0x1100060D +#define QTOPIAXK_Call 0x1100060E +#define QTOPIAXK_Hangup 0x1100060F +#define QTOPIAXK_Flip 0x11000610 + +// keyboard mapping table +static const unsigned int KeyTbl[] = { + + // misc keys + + XK_Escape, Qt::Key_Escape, + XK_Tab, Qt::Key_Tab, + XK_ISO_Left_Tab, Qt::Key_Backtab, + XK_BackSpace, Qt::Key_Backspace, + XK_Return, Qt::Key_Return, + XK_Insert, Qt::Key_Insert, + XK_Delete, Qt::Key_Delete, + XK_Clear, Qt::Key_Delete, + XK_Pause, Qt::Key_Pause, + XK_Print, Qt::Key_Print, + 0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq + 0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq + + // cursor movement + + XK_Home, Qt::Key_Home, + XK_End, Qt::Key_End, + XK_Left, Qt::Key_Left, + XK_Up, Qt::Key_Up, + XK_Right, Qt::Key_Right, + XK_Down, Qt::Key_Down, + XK_Prior, Qt::Key_PageUp, + XK_Next, Qt::Key_PageDown, + + // modifiers + + XK_Shift_L, Qt::Key_Shift, + XK_Shift_R, Qt::Key_Shift, + XK_Shift_Lock, Qt::Key_Shift, + XK_Control_L, Qt::Key_Control, + XK_Control_R, Qt::Key_Control, + XK_Meta_L, Qt::Key_Meta, + XK_Meta_R, Qt::Key_Meta, + XK_Alt_L, Qt::Key_Alt, + XK_Alt_R, Qt::Key_Alt, + XK_Caps_Lock, Qt::Key_CapsLock, + XK_Num_Lock, Qt::Key_NumLock, + XK_Scroll_Lock, Qt::Key_ScrollLock, + XK_Super_L, Qt::Key_Super_L, + XK_Super_R, Qt::Key_Super_R, + XK_Menu, Qt::Key_Menu, + XK_Hyper_L, Qt::Key_Hyper_L, + XK_Hyper_R, Qt::Key_Hyper_R, + XK_Help, Qt::Key_Help, + 0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab + 0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11) + 0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12) + + // numeric and function keypad keys + + XK_KP_Space, Qt::Key_Space, + XK_KP_Tab, Qt::Key_Tab, + XK_KP_Enter, Qt::Key_Enter, + //XK_KP_F1, Qt::Key_F1, + //XK_KP_F2, Qt::Key_F2, + //XK_KP_F3, Qt::Key_F3, + //XK_KP_F4, Qt::Key_F4, + XK_KP_Home, Qt::Key_Home, + XK_KP_Left, Qt::Key_Left, + XK_KP_Up, Qt::Key_Up, + XK_KP_Right, Qt::Key_Right, + XK_KP_Down, Qt::Key_Down, + XK_KP_Prior, Qt::Key_PageUp, + XK_KP_Next, Qt::Key_PageDown, + XK_KP_End, Qt::Key_End, + XK_KP_Begin, Qt::Key_Clear, + XK_KP_Insert, Qt::Key_Insert, + XK_KP_Delete, Qt::Key_Delete, + XK_KP_Equal, Qt::Key_Equal, + XK_KP_Multiply, Qt::Key_Asterisk, + XK_KP_Add, Qt::Key_Plus, + XK_KP_Separator, Qt::Key_Comma, + XK_KP_Subtract, Qt::Key_Minus, + XK_KP_Decimal, Qt::Key_Period, + XK_KP_Divide, Qt::Key_Slash, + + // International input method support keys + + // International & multi-key character composition + XK_ISO_Level3_Shift, Qt::Key_AltGr, + XK_Multi_key, Qt::Key_Multi_key, + XK_Codeinput, Qt::Key_Codeinput, + XK_SingleCandidate, Qt::Key_SingleCandidate, + XK_MultipleCandidate, Qt::Key_MultipleCandidate, + XK_PreviousCandidate, Qt::Key_PreviousCandidate, + + // Misc Functions + XK_Mode_switch, Qt::Key_Mode_switch, + XK_script_switch, Qt::Key_Mode_switch, + + // Japanese keyboard support + XK_Kanji, Qt::Key_Kanji, + XK_Muhenkan, Qt::Key_Muhenkan, + //XK_Henkan_Mode, Qt::Key_Henkan_Mode, + XK_Henkan_Mode, Qt::Key_Henkan, + XK_Henkan, Qt::Key_Henkan, + XK_Romaji, Qt::Key_Romaji, + XK_Hiragana, Qt::Key_Hiragana, + XK_Katakana, Qt::Key_Katakana, + XK_Hiragana_Katakana, Qt::Key_Hiragana_Katakana, + XK_Zenkaku, Qt::Key_Zenkaku, + XK_Hankaku, Qt::Key_Hankaku, + XK_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku, + XK_Touroku, Qt::Key_Touroku, + XK_Massyo, Qt::Key_Massyo, + XK_Kana_Lock, Qt::Key_Kana_Lock, + XK_Kana_Shift, Qt::Key_Kana_Shift, + XK_Eisu_Shift, Qt::Key_Eisu_Shift, + XK_Eisu_toggle, Qt::Key_Eisu_toggle, + //XK_Kanji_Bangou, Qt::Key_Kanji_Bangou, + //XK_Zen_Koho, Qt::Key_Zen_Koho, + //XK_Mae_Koho, Qt::Key_Mae_Koho, + XK_Kanji_Bangou, Qt::Key_Codeinput, + XK_Zen_Koho, Qt::Key_MultipleCandidate, + XK_Mae_Koho, Qt::Key_PreviousCandidate, + +#ifdef XK_KOREAN + // Korean keyboard support + XK_Hangul, Qt::Key_Hangul, + XK_Hangul_Start, Qt::Key_Hangul_Start, + XK_Hangul_End, Qt::Key_Hangul_End, + XK_Hangul_Hanja, Qt::Key_Hangul_Hanja, + XK_Hangul_Jamo, Qt::Key_Hangul_Jamo, + XK_Hangul_Romaja, Qt::Key_Hangul_Romaja, + //XK_Hangul_Codeinput, Qt::Key_Hangul_Codeinput, + XK_Hangul_Codeinput, Qt::Key_Codeinput, + XK_Hangul_Jeonja, Qt::Key_Hangul_Jeonja, + XK_Hangul_Banja, Qt::Key_Hangul_Banja, + XK_Hangul_PreHanja, Qt::Key_Hangul_PreHanja, + XK_Hangul_PostHanja, Qt::Key_Hangul_PostHanja, + //XK_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate, + //XK_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate, + //XK_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate, + XK_Hangul_SingleCandidate, Qt::Key_SingleCandidate, + XK_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate, + XK_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate, + XK_Hangul_Special, Qt::Key_Hangul_Special, + //XK_Hangul_switch, Qt::Key_Hangul_switch, + XK_Hangul_switch, Qt::Key_Mode_switch, +#endif // XK_KOREAN + + // dead keys + XK_dead_grave, Qt::Key_Dead_Grave, + XK_dead_acute, Qt::Key_Dead_Acute, + XK_dead_circumflex, Qt::Key_Dead_Circumflex, + XK_dead_tilde, Qt::Key_Dead_Tilde, + XK_dead_macron, Qt::Key_Dead_Macron, + XK_dead_breve, Qt::Key_Dead_Breve, + XK_dead_abovedot, Qt::Key_Dead_Abovedot, + XK_dead_diaeresis, Qt::Key_Dead_Diaeresis, + XK_dead_abovering, Qt::Key_Dead_Abovering, + XK_dead_doubleacute, Qt::Key_Dead_Doubleacute, + XK_dead_caron, Qt::Key_Dead_Caron, + XK_dead_cedilla, Qt::Key_Dead_Cedilla, + XK_dead_ogonek, Qt::Key_Dead_Ogonek, + XK_dead_iota, Qt::Key_Dead_Iota, + XK_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound, + XK_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound, + XK_dead_belowdot, Qt::Key_Dead_Belowdot, + XK_dead_hook, Qt::Key_Dead_Hook, + XK_dead_horn, Qt::Key_Dead_Horn, + + // Special keys from X.org - This include multimedia keys, + // wireless/bluetooth/uwb keys, special launcher keys, etc. + XF86XK_Back, Qt::Key_Back, + XF86XK_Forward, Qt::Key_Forward, + XF86XK_Stop, Qt::Key_Stop, + XF86XK_Refresh, Qt::Key_Refresh, + XF86XK_Favorites, Qt::Key_Favorites, + XF86XK_AudioMedia, Qt::Key_LaunchMedia, + XF86XK_OpenURL, Qt::Key_OpenUrl, + XF86XK_HomePage, Qt::Key_HomePage, + XF86XK_Search, Qt::Key_Search, + XF86XK_AudioLowerVolume, Qt::Key_VolumeDown, + XF86XK_AudioMute, Qt::Key_VolumeMute, + XF86XK_AudioRaiseVolume, Qt::Key_VolumeUp, + XF86XK_AudioPlay, Qt::Key_MediaPlay, + XF86XK_AudioStop, Qt::Key_MediaStop, + XF86XK_AudioPrev, Qt::Key_MediaPrevious, + XF86XK_AudioNext, Qt::Key_MediaNext, + XF86XK_AudioRecord, Qt::Key_MediaRecord, + XF86XK_Mail, Qt::Key_LaunchMail, + XF86XK_MyComputer, Qt::Key_Launch0, // ### Qt 5: remap properly + XF86XK_Calculator, Qt::Key_Launch1, + XF86XK_Memo, Qt::Key_Memo, + XF86XK_ToDoList, Qt::Key_ToDoList, + XF86XK_Calendar, Qt::Key_Calendar, + XF86XK_PowerDown, Qt::Key_PowerDown, + XF86XK_ContrastAdjust, Qt::Key_ContrastAdjust, + XF86XK_Standby, Qt::Key_Standby, + XF86XK_MonBrightnessUp, Qt::Key_MonBrightnessUp, + XF86XK_MonBrightnessDown, Qt::Key_MonBrightnessDown, + XF86XK_KbdLightOnOff, Qt::Key_KeyboardLightOnOff, + XF86XK_KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp, + XF86XK_KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown, + XF86XK_PowerOff, Qt::Key_PowerOff, + XF86XK_WakeUp, Qt::Key_WakeUp, + XF86XK_Eject, Qt::Key_Eject, + XF86XK_ScreenSaver, Qt::Key_ScreenSaver, + XF86XK_WWW, Qt::Key_WWW, + XF86XK_Sleep, Qt::Key_Sleep, + XF86XK_LightBulb, Qt::Key_LightBulb, + XF86XK_Shop, Qt::Key_Shop, + XF86XK_History, Qt::Key_History, + XF86XK_AddFavorite, Qt::Key_AddFavorite, + XF86XK_HotLinks, Qt::Key_HotLinks, + XF86XK_BrightnessAdjust, Qt::Key_BrightnessAdjust, + XF86XK_Finance, Qt::Key_Finance, + XF86XK_Community, Qt::Key_Community, + XF86XK_AudioRewind, Qt::Key_AudioRewind, + XF86XK_BackForward, Qt::Key_BackForward, + XF86XK_ApplicationLeft, Qt::Key_ApplicationLeft, + XF86XK_ApplicationRight, Qt::Key_ApplicationRight, + XF86XK_Book, Qt::Key_Book, + XF86XK_CD, Qt::Key_CD, + XF86XK_Calculater, Qt::Key_Calculator, + XF86XK_Clear, Qt::Key_Clear, + XF86XK_ClearGrab, Qt::Key_ClearGrab, + XF86XK_Close, Qt::Key_Close, + XF86XK_Copy, Qt::Key_Copy, + XF86XK_Cut, Qt::Key_Cut, + XF86XK_Display, Qt::Key_Display, + XF86XK_DOS, Qt::Key_DOS, + XF86XK_Documents, Qt::Key_Documents, + XF86XK_Excel, Qt::Key_Excel, + XF86XK_Explorer, Qt::Key_Explorer, + XF86XK_Game, Qt::Key_Game, + XF86XK_Go, Qt::Key_Go, + XF86XK_iTouch, Qt::Key_iTouch, + XF86XK_LogOff, Qt::Key_LogOff, + XF86XK_Market, Qt::Key_Market, + XF86XK_Meeting, Qt::Key_Meeting, + XF86XK_MenuKB, Qt::Key_MenuKB, + XF86XK_MenuPB, Qt::Key_MenuPB, + XF86XK_MySites, Qt::Key_MySites, + XF86XK_News, Qt::Key_News, + XF86XK_OfficeHome, Qt::Key_OfficeHome, + XF86XK_Option, Qt::Key_Option, + XF86XK_Paste, Qt::Key_Paste, + XF86XK_Phone, Qt::Key_Phone, + XF86XK_Reply, Qt::Key_Reply, + XF86XK_Reload, Qt::Key_Reload, + XF86XK_RotateWindows, Qt::Key_RotateWindows, + XF86XK_RotationPB, Qt::Key_RotationPB, + XF86XK_RotationKB, Qt::Key_RotationKB, + XF86XK_Save, Qt::Key_Save, + XF86XK_Send, Qt::Key_Send, + XF86XK_Spell, Qt::Key_Spell, + XF86XK_SplitScreen, Qt::Key_SplitScreen, + XF86XK_Support, Qt::Key_Support, + XF86XK_TaskPane, Qt::Key_TaskPane, + XF86XK_Terminal, Qt::Key_Terminal, + XF86XK_Tools, Qt::Key_Tools, + XF86XK_Travel, Qt::Key_Travel, + XF86XK_Video, Qt::Key_Video, + XF86XK_Word, Qt::Key_Word, + XF86XK_Xfer, Qt::Key_Xfer, + XF86XK_ZoomIn, Qt::Key_ZoomIn, + XF86XK_ZoomOut, Qt::Key_ZoomOut, + XF86XK_Away, Qt::Key_Away, + XF86XK_Messenger, Qt::Key_Messenger, + XF86XK_WebCam, Qt::Key_WebCam, + XF86XK_MailForward, Qt::Key_MailForward, + XF86XK_Pictures, Qt::Key_Pictures, + XF86XK_Music, Qt::Key_Music, + XF86XK_Battery, Qt::Key_Battery, + XF86XK_Bluetooth, Qt::Key_Bluetooth, + XF86XK_WLAN, Qt::Key_WLAN, + XF86XK_UWB, Qt::Key_UWB, + XF86XK_AudioForward, Qt::Key_AudioForward, + XF86XK_AudioRepeat, Qt::Key_AudioRepeat, + XF86XK_AudioRandomPlay, Qt::Key_AudioRandomPlay, + XF86XK_Subtitle, Qt::Key_Subtitle, + XF86XK_AudioCycleTrack, Qt::Key_AudioCycleTrack, + XF86XK_Time, Qt::Key_Time, + XF86XK_Select, Qt::Key_Select, + XF86XK_View, Qt::Key_View, + XF86XK_TopMenu, Qt::Key_TopMenu, + XF86XK_Bluetooth, Qt::Key_Bluetooth, + XF86XK_Suspend, Qt::Key_Suspend, + XF86XK_Hibernate, Qt::Key_Hibernate, + XF86XK_Launch0, Qt::Key_Launch2, // ### Qt 5: remap properly + XF86XK_Launch1, Qt::Key_Launch3, + XF86XK_Launch2, Qt::Key_Launch4, + XF86XK_Launch3, Qt::Key_Launch5, + XF86XK_Launch4, Qt::Key_Launch6, + XF86XK_Launch5, Qt::Key_Launch7, + XF86XK_Launch6, Qt::Key_Launch8, + XF86XK_Launch7, Qt::Key_Launch9, + XF86XK_Launch8, Qt::Key_LaunchA, + XF86XK_Launch9, Qt::Key_LaunchB, + XF86XK_LaunchA, Qt::Key_LaunchC, + XF86XK_LaunchB, Qt::Key_LaunchD, + XF86XK_LaunchC, Qt::Key_LaunchE, + XF86XK_LaunchD, Qt::Key_LaunchF, + XF86XK_LaunchE, Qt::Key_LaunchG, + XF86XK_LaunchF, Qt::Key_LaunchH, + + // Qtopia keys + QTOPIAXK_Select, Qt::Key_Select, + QTOPIAXK_Yes, Qt::Key_Yes, + QTOPIAXK_No, Qt::Key_No, + QTOPIAXK_Cancel, Qt::Key_Cancel, + QTOPIAXK_Printer, Qt::Key_Printer, + QTOPIAXK_Execute, Qt::Key_Execute, + QTOPIAXK_Sleep, Qt::Key_Sleep, + QTOPIAXK_Play, Qt::Key_Play, + QTOPIAXK_Zoom, Qt::Key_Zoom, + QTOPIAXK_Context1, Qt::Key_Context1, + QTOPIAXK_Context2, Qt::Key_Context2, + QTOPIAXK_Context3, Qt::Key_Context3, + QTOPIAXK_Context4, Qt::Key_Context4, + QTOPIAXK_Call, Qt::Key_Call, + QTOPIAXK_Hangup, Qt::Key_Hangup, + QTOPIAXK_Flip, Qt::Key_Flip, + + 0, 0 +}; + +static const unsigned short katakanaKeysymsToUnicode[] = { + 0x0000, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1, + 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3, + 0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, + 0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, + 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, + 0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, + 0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, + 0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C +}; + +static const unsigned short cyrillicKeysymsToUnicode[] = { + 0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, + 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f, + 0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f, + 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, + 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, + 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, + 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, + 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, + 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, + 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, + 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a +}; + +static const unsigned short greekKeysymsToUnicode[] = { + 0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, + 0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, + 0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, + 0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, + 0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, + 0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, + 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, + 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, + 0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +static const unsigned short technicalKeysymsToUnicode[] = { + 0x0000, 0x23B7, 0x250C, 0x2500, 0x2320, 0x2321, 0x2502, 0x23A1, + 0x23A3, 0x23A4, 0x23A6, 0x239B, 0x239D, 0x239E, 0x23A0, 0x23A8, + 0x23AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222B, + 0x2234, 0x221D, 0x221E, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, + 0x223C, 0x2243, 0x0000, 0x0000, 0x0000, 0x21D4, 0x21D2, 0x2261, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221A, 0x0000, + 0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222A, 0x2227, 0x2228, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, + 0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193, 0x0000 +}; + +static const unsigned short specialKeysymsToUnicode[] = { + 0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x0000, 0x0000, + 0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA, + 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C, + 0x2502, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +static const unsigned short publishingKeysymsToUnicode[] = { + 0x0000, 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, + 0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, + 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, + 0x2105, 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000, + 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, + 0x0000, 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af, + 0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, + 0x0000, 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae, + 0x25e6, 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa, + 0x25b2, 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000, + 0x2720, 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642, + 0x2640, 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e, 0x0000 +}; + +static const unsigned short aplKeysymsToUnicode[] = { + 0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003e, 0x0000, + 0x2228, 0x2227, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00af, 0x0000, 0x22a5, 0x2229, 0x230a, 0x0000, 0x005f, 0x0000, + 0x0000, 0x0000, 0x2218, 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb, + 0x0000, 0x0000, 0x0000, 0x2308, 0x0000, 0x0000, 0x222a, 0x0000, + 0x2283, 0x0000, 0x2282, 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x22a3, 0x0000, 0x0000, 0x0000 +}; + +static const unsigned short koreanKeysymsToUnicode[] = { + 0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, + 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f, + 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147, + 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f, + 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, + 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, + 0x3160, 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab, + 0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, + 0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, + 0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d, + 0x3171, 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e, + 0x11eb, 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9 +}; + +static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4) +{ + switch (byte3) { + case 0x04: + // katakana + if (byte4 > 0xa0 && byte4 < 0xe0) + return QChar(katakanaKeysymsToUnicode[byte4 - 0xa0]); + else if (byte4 == 0x7e) + return QChar(0x203e); // Overline + break; + case 0x06: + // russian, use lookup table + if (byte4 > 0xa0) + return QChar(cyrillicKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x07: + // greek + if (byte4 > 0xa0) + return QChar(greekKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x08: + // technical + if (byte4 > 0xa0) + return QChar(technicalKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x09: + // special + if (byte4 >= 0xe0) + return QChar(specialKeysymsToUnicode[byte4 - 0xe0]); + break; + case 0x0a: + // publishing + if (byte4 > 0xa0) + return QChar(publishingKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x0b: + // APL + if (byte4 > 0xa0) + return QChar(aplKeysymsToUnicode[byte4 - 0xa0]); + break; + case 0x0e: + // Korean + if (byte4 > 0xa0) + return QChar(koreanKeysymsToUnicode[byte4 - 0xa0]); + break; + default: + break; + } + return QChar(0x0); +} + +Qt::KeyboardModifiers QTestLiteKeyboard::translateModifiers(int s) +{ + Qt::KeyboardModifiers ret = 0; + if (s & ShiftMask) + ret |= Qt::ShiftModifier; + if (s & ControlMask) + ret |= Qt::ControlModifier; + if (s & m_alt_mask) + ret |= Qt::AltModifier; + if (s & m_meta_mask) + ret |= Qt::MetaModifier; +// if (s & m_mode_switch_mask) //doesn't seem to work correctly +// ret |= Qt::GroupSwitchModifier; + return ret; +} + +void QTestLiteKeyboard::setMask(KeySym sym, uint mask) +{ + if (m_alt_mask == 0 + && m_meta_mask != mask + && m_super_mask != mask + && m_hyper_mask != mask + && (sym == XK_Alt_L || sym == XK_Alt_R)) { + m_alt_mask = mask; + } + if (m_meta_mask == 0 + && m_alt_mask != mask + && m_super_mask != mask + && m_hyper_mask != mask + && (sym == XK_Meta_L || sym == XK_Meta_R)) { + m_meta_mask = mask; + } + if (m_super_mask == 0 + && m_alt_mask != mask + && m_meta_mask != mask + && m_hyper_mask != mask + && (sym == XK_Super_L || sym == XK_Super_R)) { + m_super_mask = mask; + } + if (m_hyper_mask == 0 + && m_alt_mask != mask + && m_meta_mask != mask + && m_super_mask != mask + && (sym == XK_Hyper_L || sym == XK_Hyper_R)) { + m_hyper_mask = mask; + } + if (m_mode_switch_mask == 0 + && m_alt_mask != mask + && m_meta_mask != mask + && m_super_mask != mask + && m_hyper_mask != mask + && sym == XK_Mode_switch) { + m_mode_switch_mask = mask; + } + if (m_num_lock_mask == 0 + && sym == XK_Num_Lock) { + m_num_lock_mask = mask; + } +} + +int QTestLiteKeyboard::translateKeySym(uint key) const +{ + int code = -1; + int i = 0; // any other keys + while (KeyTbl[i]) { + if (key == KeyTbl[i]) { + code = (int)KeyTbl[i+1]; + break; + } + i += 2; + } + if (m_meta_mask) { + // translate Super/Hyper keys to Meta if we're using them as the MetaModifier + if (m_meta_mask == m_super_mask && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) { + code = Qt::Key_Meta; + } else if (m_meta_mask == m_hyper_mask && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) { + code = Qt::Key_Meta; + } + } + return code; +} + +QString QTestLiteKeyboard::translateKeySym(KeySym keysym, uint xmodifiers, + int &code, Qt::KeyboardModifiers &modifiers, + QByteArray &chars, int &count) +{ + // all keysyms smaller than 0xff00 are actally keys that can be mapped to unicode chars + + QTextCodec *mapper = QTextCodec::codecForLocale(); + QChar converted; + + if (/*count == 0 &&*/ keysym < 0xff00) { + unsigned char byte3 = (unsigned char)(keysym >> 8); + int mib = -1; + switch(byte3) { + case 0: // Latin 1 + case 1: // Latin 2 + case 2: //latin 3 + case 3: // latin4 + mib = byte3 + 4; break; + case 5: // arabic + mib = 82; break; + case 12: // Hebrew + mib = 85; break; + case 13: // Thai + mib = 2259; break; + case 4: // kana + case 6: // cyrillic + case 7: // greek + case 8: // technical, no mapping here at the moment + case 9: // Special + case 10: // Publishing + case 11: // APL + case 14: // Korean, no mapping + mib = -1; // manual conversion + mapper= 0; +#if !defined(QT_NO_XIM) + converted = keysymToUnicode(byte3, keysym & 0xff); +#endif + case 0x20: + // currency symbols + if (keysym >= 0x20a0 && keysym <= 0x20ac) { + mib = -1; // manual conversion + mapper = 0; + converted = (uint)keysym; + } + break; + default: + break; + } + if (mib != -1) { + mapper = QTextCodec::codecForMib(mib); + if (chars.isEmpty()) + chars.resize(1); + chars[0] = (unsigned char) (keysym & 0xff); // get only the fourth bit for conversion later + count = 1; + } + } else if (keysym >= 0x1000000 && keysym <= 0x100ffff) { + converted = (ushort) (keysym - 0x1000000); + mapper = 0; + } + if (count < (int)chars.size()-1) + chars[count] = '\0'; + + QString text; + if (!mapper && converted.unicode() != 0x0) { + text = converted; + } else if (!chars.isEmpty()) { + // convert chars (8bit) to text (unicode). + if (mapper) + text = mapper->toUnicode(chars.data(), count, 0); + if (text.isEmpty()) { + // no mapper, or codec couldn't convert to unicode (this + // can happen when running in the C locale or with no LANG + // set). try converting from latin-1 + text = QString::fromLatin1(chars); + } + } + + modifiers = translateModifiers(xmodifiers); + + // Commentary in X11/keysymdef says that X codes match ASCII, so it + // is safe to use the locale functions to process X codes in ISO8859-1. + // + // This is mainly for compatibility - applications should not use the + // Qt keycodes between 128 and 255, but should rather use the + // QKeyEvent::text(). + // + if (keysym < 128 || (keysym < 256 && (!mapper || mapper->mibEnum()==4))) { + // upper-case key, if known + code = isprint((int)keysym) ? toupper((int)keysym) : 0; + } else if (keysym >= XK_F1 && keysym <= XK_F35) { + // function keys + code = Qt::Key_F1 + ((int)keysym - XK_F1); + } else if (keysym >= XK_KP_Space && keysym <= XK_KP_9) { + if (keysym >= XK_KP_0) { + // numeric keypad keys + code = Qt::Key_0 + ((int)keysym - XK_KP_0); + } else { + code = translateKeySym(keysym); + } + modifiers |= Qt::KeypadModifier; + } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f && text.unicode()->unicode() != 0x7f && !(keysym >= XK_dead_grave && keysym <= XK_dead_horn)) { + code = text.unicode()->toUpper().unicode(); + } else { + // any other keys + code = translateKeySym(keysym); + + if (code == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) { + // map shift+tab to shift+backtab, QShortcutMap knows about it + // and will handle it. + code = Qt::Key_Backtab; + text = QString(); + } + } + + return text; +} + +QTestLiteKeyboard::QTestLiteKeyboard(QTestLiteScreen *screen) + : m_screen(screen) + , m_alt_mask(0) + , m_super_mask(0) + , m_hyper_mask(0) + , m_meta_mask(0) +{ + changeLayout(); +} + +void QTestLiteKeyboard::changeLayout() +{ + XkbDescPtr xkbDesc = XkbGetMap(m_screen->display(), XkbAllClientInfoMask, XkbUseCoreKbd); + for (int i = xkbDesc->min_key_code; i < xkbDesc->max_key_code; ++i) { + const uint mask = xkbDesc->map->modmap ? xkbDesc->map->modmap[i] : 0; + if (mask == 0) { + // key is not bound to a modifier + continue; + } + + for (int j = 0; j < XkbKeyGroupsWidth(xkbDesc, i); ++j) { + KeySym keySym = XkbKeySym(xkbDesc, i, j); + if (keySym == NoSymbol) + continue; + setMask(keySym, mask); + } + } + XkbFreeKeyboard(xkbDesc, XkbAllComponentsMask, true); + +} + +static Qt::KeyboardModifiers modifierFromKeyCode(int qtcode) +{ + switch (qtcode) { + case Qt::Key_Control: + return Qt::ControlModifier; + case Qt::Key_Alt: + return Qt::AltModifier; + case Qt::Key_Shift: + return Qt::ShiftModifier; + case Qt::Key_Meta: + return Qt::MetaModifier; + default: + return Qt::NoModifier; + } +} + +void QTestLiteKeyboard::handleKeyEvent(QWidget *widget, QEvent::Type type, XKeyEvent *ev) +{ + int qtcode = 0; + Qt::KeyboardModifiers modifiers = translateModifiers(ev->state); + QByteArray chars; + chars.resize(513); + int count = 0; + KeySym keySym; + count = XLookupString(ev,chars.data(),chars.size(),&keySym,0); + QString text = translateKeySym(keySym,ev->state,qtcode,modifiers,chars,count); + QWindowSystemInterface::handleKeyEvent(widget,ev->time,type,qtcode,modifiers,text.left(count)); +} diff --git a/src/plugins/platforms/testlite/qtestlitekeyboard.h b/src/plugins/platforms/testlite/qtestlitekeyboard.h new file mode 100644 index 0000000..6873a09 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitekeyboard.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTLITEKEYBOARD_H +#define QTESTLITEKEYBOARD_H + +#include "qtestliteintegration.h" + +class QTestLiteKeyboard +{ +public: + QTestLiteKeyboard(QTestLiteScreen *screen); + + void changeLayout(); + + void handleKeyEvent(QWidget *widget, QEvent::Type type, XKeyEvent *ev); + + Qt::KeyboardModifiers translateModifiers(int s); + +private: + + void setMask(KeySym sym, uint mask); + int translateKeySym(uint key) const; + QString translateKeySym(KeySym keysym, uint xmodifiers, + int &code, Qt::KeyboardModifiers &modifiers, + QByteArray &chars, int &count); + + QTestLiteScreen *m_screen; + + uint m_alt_mask; + uint m_super_mask; + uint m_hyper_mask; + uint m_meta_mask; + uint m_mode_switch_mask; + uint m_num_lock_mask; +}; + +#endif // QTESTLITEKEYBOARD_H diff --git a/src/plugins/platforms/testlite/qtestlitemime.cpp b/src/plugins/platforms/testlite/qtestlitemime.cpp new file mode 100644 index 0000000..c509991 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitemime.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtestlitemime.h" + +#include "qtestlitestaticinfo.h" +#include "qtestlitescreen.h" + +#include <QtCore/QTextCodec> +#include <QtGui/QImageWriter> +#include <QtCore/QBuffer> + +QTestLiteMime::QTestLiteMime() + : QInternalMimeData() +{ } + +QTestLiteMime::~QTestLiteMime() +{} + + + + + +QString QTestLiteMime::mimeAtomToString(Display *display, Atom a) +{ + if (!a) return 0; + + if (a == XA_STRING || a == QTestLiteStatic::atom(QTestLiteStatic::UTF8_STRING)) { + return "text/plain"; // some Xdnd clients are dumb + } + char *atom = XGetAtomName(display, a); + QString result = QString::fromLatin1(atom); + XFree(atom); + return result; +} + +Atom QTestLiteMime::mimeStringToAtom(Display *display, const QString &mimeType) +{ + if (mimeType.isEmpty()) + return 0; + return XInternAtom(display, mimeType.toLatin1().constData(), False); +} + +QStringList QTestLiteMime::mimeFormatsForAtom(Display *display, Atom a) +{ + QStringList formats; + if (a) { + QString atomName = mimeAtomToString(display, a); + formats.append(atomName); + + // special cases for string type + if (a == QTestLiteStatic::atom(QTestLiteStatic::UTF8_STRING) + || a == XA_STRING + || a == QTestLiteStatic::atom(QTestLiteStatic::TEXT) + || a == QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT)) + formats.append(QLatin1String("text/plain")); + + // special cases for uris + if (atomName == QLatin1String("text/x-moz-url")) + formats.append(QLatin1String("text/uri-list")); + + // special case for images + if (a == XA_PIXMAP) + formats.append(QLatin1String("image/ppm")); + } + return formats; +} + +bool QTestLiteMime::mimeDataForAtom(Display *display, Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat) +{ + bool ret = false; + *atomFormat = a; + *dataFormat = 8; + QString atomName = mimeAtomToString(display, a); + if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) { + *data = QInternalMimeData::renderDataHelper(atomName, mimeData); + if (atomName == QLatin1String("application/x-color")) + *dataFormat = 16; + ret = true; + } else { + if ((a == QTestLiteStatic::atom(QTestLiteStatic::UTF8_STRING) + || a == XA_STRING + || a == QTestLiteStatic::atom(QTestLiteStatic::TEXT) + || a == QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT)) + && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) { + if (a == QTestLiteStatic::atom(QTestLiteStatic::UTF8_STRING)){ + *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData); + ret = true; + } else if (a == XA_STRING) { + *data = QString::fromUtf8(QInternalMimeData::renderDataHelper( + QLatin1String("text/plain"), mimeData)).toLocal8Bit(); + ret = true; + } else if (a == QTestLiteStatic::atom(QTestLiteStatic::TEXT) + || a == QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT)) { + // the ICCCM states that TEXT and COMPOUND_TEXT are in the + // encoding of choice, so we choose the encoding of the locale + QByteArray strData = QString::fromUtf8(QInternalMimeData::renderDataHelper( + QLatin1String("text/plain"), mimeData)).toLocal8Bit(); + char *list[] = { strData.data(), NULL }; + + XICCEncodingStyle style = (a == QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT)) + ? XCompoundTextStyle : XStdICCTextStyle; + XTextProperty textprop; + if (list[0] != NULL + && XmbTextListToTextProperty(display, list, 1, style, + &textprop) == Success) { + *atomFormat = textprop.encoding; + *dataFormat = textprop.format; + *data = QByteArray((const char *) textprop.value, textprop.nitems * textprop.format / 8); + ret = true; + + XFree(textprop.value); + } + } + } else if (atomName == QLatin1String("text/x-moz-url") && + QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) { + QByteArray uri = QInternalMimeData::renderDataHelper( + QLatin1String("text/uri-list"), mimeData).split('\n').first(); + QString mozUri = QString::fromLatin1(uri, uri.size()); + mozUri += QLatin1Char('\n'); + *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2); + ret = true; + } else if ((a == XA_PIXMAP || a == XA_BITMAP) && mimeData->hasImage()) { + ret = true; + } + } + return ret && data != 0; +} + +QList<Atom> QTestLiteMime::mimeAtomsForFormat(Display *display, const QString &format) +{ + QList<Atom> atoms; + atoms.append(mimeStringToAtom(display, format)); + + // special cases for strings + if (format == QLatin1String("text/plain")) { + atoms.append(QTestLiteStatic::atom(QTestLiteStatic::UTF8_STRING)); + atoms.append(XA_STRING); + atoms.append(QTestLiteStatic::atom(QTestLiteStatic::TEXT)); + atoms.append(QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT)); + } + + // special cases for uris + if (format == QLatin1String("text/uri-list")) { + atoms.append(mimeStringToAtom(display,QLatin1String("text/x-moz-url"))); + } + + //special cases for images + if (format == QLatin1String("image/ppm")) + atoms.append(XA_PIXMAP); + if (format == QLatin1String("image/pbm")) + atoms.append(XA_BITMAP); + + return atoms; +} + +QVariant QTestLiteMime::mimeConvertToFormat(Display *display, Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding) +{ + QString atomName = mimeAtomToString(display,a); + if (atomName == format) + return data; + + if (!encoding.isEmpty() + && atomName == format + QLatin1String(";charset=") + QString::fromLatin1(encoding)) { + + if (requestedType == QVariant::String) { + QTextCodec *codec = QTextCodec::codecForName(encoding); + if (codec) + return codec->toUnicode(data); + } + + return data; + } + + // special cases for string types + if (format == QLatin1String("text/plain")) { + if (a == QTestLiteStatic::atom(QTestLiteStatic::UTF8_STRING)) + return QString::fromUtf8(data); + if (a == XA_STRING) + return QString::fromLatin1(data); + if (a == QTestLiteStatic::atom(QTestLiteStatic::TEXT) + || a == QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT)) + // #### might be wrong for COMPUND_TEXT + return QString::fromLocal8Bit(data, data.size()); + } + + // special case for uri types + if (format == QLatin1String("text/uri-list")) { + if (atomName == QLatin1String("text/x-moz-url")) { + // we expect this as utf16 <url><space><title> + // the first part is a url that should only contain ascci char + // so it should be safe to check that the second char is 0 + // to verify that it is utf16 + if (data.size() > 1 && data.at(1) == 0) + return QString::fromRawData((const QChar *)data.constData(), + data.size() / 2).split(QLatin1Char('\n')).first().toLatin1(); + } + } + + // special cas for images + if (format == QLatin1String("image/ppm")) { + if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) { + Pixmap xpm = *((Pixmap*)data.data()); + if (!xpm) + return QByteArray(); + Window root; + int x; + int y; + uint width; + uint height; + uint border_width; + uint depth; + + XGetGeometry(display, xpm, &root, &x, &y, &width, &height, &border_width, &depth); + XImage *ximg = XGetImage(display,xpm,x,y,width,height,AllPlanes,depth==1 ? XYPixmap : ZPixmap); + QImage qimg = QTestLiteStatic::qimageFromXImage(ximg); + XDestroyImage(ximg); + + QImageWriter imageWriter; + imageWriter.setFormat("PPMRAW"); + QBuffer buf; + buf.open(QIODevice::WriteOnly); + imageWriter.setDevice(&buf); + imageWriter.write(qimg); + return buf.buffer(); + } + } + return QVariant(); +} + +Atom QTestLiteMime::mimeAtomForFormat(Display *display, const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *requestedEncoding) +{ + requestedEncoding->clear(); + + // find matches for string types + if (format == QLatin1String("text/plain")) { + if (atoms.contains(QTestLiteStatic::atom(QTestLiteStatic::UTF8_STRING))) + return QTestLiteStatic::atom(QTestLiteStatic::UTF8_STRING); + if (atoms.contains(QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT))) + return QTestLiteStatic::atom(QTestLiteStatic::COMPOUND_TEXT); + if (atoms.contains(QTestLiteStatic::atom(QTestLiteStatic::TEXT))) + return QTestLiteStatic::atom(QTestLiteStatic::TEXT); + if (atoms.contains(XA_STRING)) + return XA_STRING; + } + + // find matches for uri types + if (format == QLatin1String("text/uri-list")) { + Atom a = mimeStringToAtom(display,format); + if (a && atoms.contains(a)) + return a; + a = mimeStringToAtom(display,QLatin1String("text/x-moz-url")); + if (a && atoms.contains(a)) + return a; + } + + // find match for image + if (format == QLatin1String("image/ppm")) { + if (atoms.contains(XA_PIXMAP)) + return XA_PIXMAP; + } + + // for string/text requests try to use a format with a well-defined charset + // first to avoid encoding problems + if (requestedType == QVariant::String + && format.startsWith(QLatin1String("text/")) + && !format.contains(QLatin1String("charset="))) { + + QString formatWithCharset = format; + formatWithCharset.append(QLatin1String(";charset=utf-8")); + + Atom a = mimeStringToAtom(display,formatWithCharset); + if (a && atoms.contains(a)) { + *requestedEncoding = "utf-8"; + return a; + } + } + + Atom a = mimeStringToAtom(display,format); + if (a && atoms.contains(a)) + return a; + + return 0; +} diff --git a/src/plugins/platforms/testlite/qtestlitemime.h b/src/plugins/platforms/testlite/qtestlitemime.h new file mode 100644 index 0000000..f11070f --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitemime.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTLITEMIME_H +#define QTESTLITEMIME_H + +#include <private/qdnd_p.h> + +#include <QtGui/QClipboard> + +#include "qtestliteintegration.h" +#include "qtestliteclipboard.h" + +class QTestLiteMime : public QInternalMimeData { + Q_OBJECT +public: + QTestLiteMime(); + ~QTestLiteMime(); + + static QList<Atom> mimeAtomsForFormat(Display *display, const QString &format); + static QString mimeAtomToString(Display *display, Atom a); + static bool mimeDataForAtom(Display *display, Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat); + static QStringList mimeFormatsForAtom(Display *display, Atom a); + static Atom mimeStringToAtom(Display *display, const QString &mimeType); + static QVariant mimeConvertToFormat(Display *display, Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding); + static Atom mimeAtomForFormat(Display *display, const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *requestedEncoding); +}; + +#endif // QTESTLITEMIME_H diff --git a/src/plugins/platforms/testlite/qtestlitescreen.cpp b/src/plugins/platforms/testlite/qtestlitescreen.cpp new file mode 100644 index 0000000..c211ee6 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitescreen.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtestlitescreen.h" + +#include "qtestlitecursor.h" +#include "qtestlitewindow.h" +#include "qtestlitekeyboard.h" +#include "qtestlitestaticinfo.h" +#include "qtestliteclipboard.h" + +#include <QtCore/QDebug> +#include <QtCore/QSocketNotifier> +#include <QtCore/QElapsedTimer> + +#include <private/qapplication_p.h> + +#include <X11/extensions/Xfixes.h> + +QT_BEGIN_NAMESPACE + +static int (*original_x_errhandler)(Display *dpy, XErrorEvent *); +static bool seen_badwindow; + +static int qt_x_errhandler(Display *dpy, XErrorEvent *err) +{ + +qDebug() << "qt_x_errhandler" << err->error_code; + + switch (err->error_code) { + case BadAtom: +#if 0 + if (err->request_code == 20 /* X_GetProperty */ + && (err->resourceid == XA_RESOURCE_MANAGER + || err->resourceid == XA_RGB_DEFAULT_MAP + || err->resourceid == ATOM(_NET_SUPPORTED) + || err->resourceid == ATOM(_NET_SUPPORTING_WM_CHECK) + || err->resourceid == ATOM(KDE_FULL_SESSION) + || err->resourceid == ATOM(KWIN_RUNNING) + || err->resourceid == ATOM(XdndProxy) + || err->resourceid == ATOM(XdndAware)) + + + ) { + // Perhaps we're running under SECURITY reduction? :/ + return 0; + } +#endif + qDebug() << "BadAtom"; + break; + + case BadWindow: + if (err->request_code == 2 /* X_ChangeWindowAttributes */ + || err->request_code == 38 /* X_QueryPointer */) { + for (int i = 0; i < ScreenCount(dpy); ++i) { + if (err->resourceid == RootWindow(dpy, i)) { + // Perhaps we're running under SECURITY reduction? :/ + return 0; + } + } + } + seen_badwindow = true; + if (err->request_code == 25 /* X_SendEvent */) { + for (int i = 0; i < ScreenCount(dpy); ++i) { + if (err->resourceid == RootWindow(dpy, i)) { + // Perhaps we're running under SECURITY reduction? :/ + return 0; + } + } +#if 0 + if (X11->xdndHandleBadwindow()) { + qDebug("xdndHandleBadwindow returned true"); + return 0; + } +#endif + } +#if 0 + if (X11->ignore_badwindow) + return 0; +#endif + break; + + case BadMatch: + if (err->request_code == 42 /* X_SetInputFocus */) + return 0; + break; + + default: +#if 0 //!defined(QT_NO_XINPUT) + if (err->request_code == X11->xinput_major + && err->error_code == (X11->xinput_errorbase + XI_BadDevice) + && err->minor_code == 3 /* X_OpenDevice */) { + return 0; + } +#endif + break; + } + + char errstr[256]; + XGetErrorText( dpy, err->error_code, errstr, 256 ); + char buffer[256]; + char request_str[256]; + qsnprintf(buffer, 256, "%d", err->request_code); + XGetErrorDatabaseText(dpy, "XRequest", buffer, "", request_str, 256); + if (err->request_code < 128) { + // X error for a normal protocol request + qWarning( "X Error: %s %d\n" + " Major opcode: %d (%s)\n" + " Resource id: 0x%lx", + errstr, err->error_code, + err->request_code, + request_str, + err->resourceid ); + } else { + // X error for an extension request + const char *extensionName = 0; +#if 0 + if (err->request_code == X11->xrender_major) + extensionName = "RENDER"; + else if (err->request_code == X11->xrandr_major) + extensionName = "RANDR"; + else if (err->request_code == X11->xinput_major) + extensionName = "XInputExtension"; + else if (err->request_code == X11->mitshm_major) + extensionName = "MIT-SHM"; +#endif + char minor_str[256]; + if (extensionName) { + qsnprintf(buffer, 256, "%s.%d", extensionName, err->minor_code); + XGetErrorDatabaseText(dpy, "XRequest", buffer, "", minor_str, 256); + } else { + extensionName = "Uknown extension"; + qsnprintf(minor_str, 256, "Unknown request"); + } + qWarning( "X Error: %s %d\n" + " Extension: %d (%s)\n" + " Minor opcode: %d (%s)\n" + " Resource id: 0x%lx", + errstr, err->error_code, + err->request_code, + extensionName, + err->minor_code, + minor_str, + err->resourceid ); + } + + // ### we really should distinguish between severe, non-severe and + // ### application specific errors + + return 0; +} + +QTestLiteScreen::QTestLiteScreen() + : mFormat(QImage::Format_RGB32) +{ + char *display_name = getenv("DISPLAY"); + mDisplay = XOpenDisplay(display_name); + mDisplayName = QString::fromLocal8Bit(display_name); + if (!mDisplay) { + fprintf(stderr, "Cannot connect to X server: %s\n", + display_name); + exit(1); + } + +#ifndef DONT_USE_MIT_SHM + Status MIT_SHM_extension_supported = XShmQueryExtension (mDisplay); + Q_ASSERT(MIT_SHM_extension_supported == True); +#endif + original_x_errhandler = XSetErrorHandler(qt_x_errhandler); + + if (qgetenv("DO_X_SYNCHRONIZE").toInt()) + XSynchronize(mDisplay, true); + + mScreen = DefaultScreen(mDisplay); + XSelectInput(mDisplay,rootWindow(), KeymapStateMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask); + int width = DisplayWidth(mDisplay, mScreen); + int height = DisplayHeight(mDisplay, mScreen); + mGeometry = QRect(0,0,width,height); + + int physicalWidth = DisplayWidthMM(mDisplay, mScreen); + int physicalHeight = DisplayHeightMM(mDisplay, mScreen); + mPhysicalSize = QSize(physicalWidth,physicalHeight); + + int xSocketNumber = XConnectionNumber(mDisplay); + + mDepth = DefaultDepth(mDisplay,mScreen); +#ifdef MYX11_DEBUG + qDebug() << "X socket:"<< xSocketNumber; +#endif + QSocketNotifier *sock = new QSocketNotifier(xSocketNumber, QSocketNotifier::Read, this); + connect(sock, SIGNAL(activated(int)), this, SLOT(eventDispatcher())); + + mCursor = new QTestLiteCursor(this); + mKeyboard = new QTestLiteKeyboard(this); +} + +QTestLiteScreen::~QTestLiteScreen() +{ + delete mCursor; + XCloseDisplay(mDisplay); +} + +#ifdef KeyPress +#undef KeyPress +#endif +#ifdef KeyRelease +#undef KeyRelease +#endif + +bool QTestLiteScreen::handleEvent(XEvent *xe) +{ + int quit = false; + QTestLiteWindow *platformWindow = 0; + QWidget *widget = QWidget::find(xe->xany.window); + if (widget) { + platformWindow = static_cast<QTestLiteWindow *>(widget->platformWindow()); + } + + Atom wmProtocolsAtom = QTestLiteStatic::atom(QTestLiteStatic::WM_PROTOCOLS); + Atom wmDeleteWindowAtom = QTestLiteStatic::atom(QTestLiteStatic::WM_DELETE_WINDOW); + switch (xe->type) { + + case ClientMessage: + if (xe->xclient.format == 32 && xe->xclient.message_type == wmProtocolsAtom) { + Atom a = xe->xclient.data.l[0]; + if (a == wmDeleteWindowAtom) + platformWindow->handleCloseEvent(); + } + break; + + case Expose: + if (platformWindow) + if (xe->xexpose.count == 0) + platformWindow->paintEvent(); + break; + case ConfigureNotify: + if (platformWindow) + platformWindow->resizeEvent(&xe->xconfigure); + break; + + case ButtonPress: + if (platformWindow) + platformWindow->mousePressEvent(&xe->xbutton); + break; + + case ButtonRelease: + if (platformWindow) + platformWindow->handleMouseEvent(QEvent::MouseButtonRelease, &xe->xbutton); + break; + + case MotionNotify: + if (platformWindow) + platformWindow->handleMouseEvent(QEvent::MouseMove, &xe->xbutton); + break; + + case XKeyPress: + mKeyboard->handleKeyEvent(widget,QEvent::KeyPress, &xe->xkey); + break; + + case XKeyRelease: + mKeyboard->handleKeyEvent(widget,QEvent::KeyRelease, &xe->xkey); + break; + + case EnterNotify: + if (platformWindow) + platformWindow->handleEnterEvent(); + break; + + case LeaveNotify: + if (platformWindow) + platformWindow->handleLeaveEvent(); + break; + + case XFocusIn: + if (platformWindow) + platformWindow->handleFocusInEvent(); + break; + + case XFocusOut: + if (platformWindow) + platformWindow->handleFocusOutEvent(); + break; + + case PropertyNotify: + break; + + case SelectionClear: + qDebug() << "Selection Clear!!!"; + break; + case SelectionRequest: + handleSelectionRequest(xe); + break; + case SelectionNotify: + qDebug() << "Selection Notify!!!!"; + + break; + + + default: +#ifdef MYX11_DEBUG + qDebug() << hex << xe->xany.window << "Other X event" << xe->type; +#endif + break; + } + + return quit; +} + +static Bool checkForClipboardEvents(Display *, XEvent *e, XPointer) +{ + Atom clipboard = QTestLiteStatic::atom(QTestLiteStatic::CLIPBOARD); + return ((e->type == SelectionRequest && (e->xselectionrequest.selection == XA_PRIMARY + || e->xselectionrequest.selection == clipboard)) + || (e->type == SelectionClear && (e->xselectionclear.selection == XA_PRIMARY + || e->xselectionclear.selection == clipboard))); +} + +bool QTestLiteScreen::waitForClipboardEvent(Window win, int type, XEvent *event, int timeout) +{ + QElapsedTimer timer; + timer.start(); + do { + if (XCheckTypedWindowEvent(mDisplay,win,type,event)) + return true; + + // process other clipboard events, since someone is probably requesting data from us + XEvent e; + if (XCheckIfEvent(mDisplay, &e, checkForClipboardEvents, 0)) + handleEvent(&e); + + XFlush(mDisplay); + + // sleep 50 ms, so we don't use up CPU cycles all the time. + struct timeval usleep_tv; + usleep_tv.tv_sec = 0; + usleep_tv.tv_usec = 50000; + select(0, 0, 0, 0, &usleep_tv); + } while (timer.elapsed() < timeout); + return false; +} + +void QTestLiteScreen::eventDispatcher() +{ + ulong marker = XNextRequest(mDisplay); + // int i = 0; + while (XPending(mDisplay)) { + XEvent event; + XNextEvent(mDisplay, &event); + /* done = */ + handleEvent(&event); + + if (event.xany.serial >= marker) { + #ifdef MYX11_DEBUG + qDebug() << "potential livelock averted"; + #endif + #if 0 + if (XEventsQueued(mDisplay, QueuedAfterFlush)) { + qDebug() << " with events queued"; + QTimer::singleShot(0, this, SLOT(eventDispatcher())); + } + #endif + break; + } + } +} + +QImage QTestLiteScreen::grabWindow(Window window, int x, int y, int w, int h) +{ + if (w == 0 || h ==0) + return QImage(); + + //WinId 0 means the desktop widget + if (!window) + window = rootWindow(); + + XWindowAttributes window_attr; + if (!XGetWindowAttributes(mDisplay, window, &window_attr)) + return QImage(); + + if (w < 0) + w = window_attr.width - x; + if (h < 0) + h = window_attr.height - y; + + // Ideally, we should also limit ourselves to the screen area, but the Qt docs say + // that it's "unsafe" to go outside the screen, so we can ignore that problem. + + //We're definitely not optimizing for speed... + XImage *xi = XGetImage(mDisplay, window, x, y, w, h, AllPlanes, ZPixmap); + + if (!xi) + return QImage(); + + //taking a copy to make sure we have ownership -- not fast + QImage result = QImage( (uchar*) xi->data, xi->width, xi->height, xi->bytes_per_line, QImage::Format_RGB32 ).copy(); + + XDestroyImage(xi); + + return result; +} + +QTestLiteScreen * QTestLiteScreen::testLiteScreenForWidget(QWidget *widget) +{ + QPlatformScreen *platformScreen = platformScreenForWidget(widget); + return static_cast<QTestLiteScreen *>(platformScreen); +} + +Display * QTestLiteScreen::display() const +{ + return mDisplay; +} + +int QTestLiteScreen::xScreenNumber() const +{ + return mScreen; +} + +QTestLiteKeyboard * QTestLiteScreen::keyboard() const +{ + return mKeyboard; +} + +void QTestLiteScreen::handleSelectionRequest(XEvent *event) +{ + QPlatformIntegration *integration = QApplicationPrivate::platformIntegration(); + QTestLiteClipboard *clipboard = static_cast<QTestLiteClipboard *>(integration->clipboard()); + clipboard->handleSelectionRequest(event); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/testlite/qtestlitescreen.h b/src/plugins/platforms/testlite/qtestlitescreen.h new file mode 100644 index 0000000..860a67c --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitescreen.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTLITESCREEN_H +#define QTESTLITESCREEN_H + +#include <QtGui/QPlatformScreen> +#include "qtestliteintegration.h" + +QT_BEGIN_NAMESPACE + +class QTestLiteCursor; +class QTestLiteKeyboard; + +class QTestLiteScreen : public QPlatformScreen +{ + Q_OBJECT +public: + QTestLiteScreen(); + + ~QTestLiteScreen(); + + QString displayName() const { return mDisplayName; } + + QRect geometry() const { return mGeometry; } + int depth() const { return mDepth; } + QImage::Format format() const { return mFormat; } + QSize physicalSize() const { return mPhysicalSize; } + + Window rootWindow() { return RootWindow(mDisplay, mScreen); } + unsigned long blackPixel() { return BlackPixel(mDisplay, mScreen); } + unsigned long whitePixel() { return WhitePixel(mDisplay, mScreen); } + + bool handleEvent(XEvent *xe); + bool waitForClipboardEvent(Window win, int type, XEvent *event, int timeout); + + QImage grabWindow(Window window, int x, int y, int w, int h); + + static QTestLiteScreen *testLiteScreenForWidget(QWidget *widget); + + Display *display() const; + int xScreenNumber() const; + + QTestLiteKeyboard *keyboard() const; + +public slots: + void eventDispatcher(); + +private: + + void handleSelectionRequest(XEvent *event); + QString mDisplayName; + QRect mGeometry; + QSize mPhysicalSize; + int mDepth; + QImage::Format mFormat; + QTestLiteCursor *mCursor; + QTestLiteKeyboard *mKeyboard; + + Display * mDisplay; + int mScreen; +}; + +QT_END_NAMESPACE + +#endif // QTESTLITESCREEN_H diff --git a/src/plugins/platforms/testlite/qtestlitestaticinfo.cpp b/src/plugins/platforms/testlite/qtestlitestaticinfo.cpp new file mode 100644 index 0000000..2c6404d --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitestaticinfo.cpp @@ -0,0 +1,511 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtestlitestaticinfo.h" +#include "qtestlitescreen.h" + +#include <qplatformdefs.h> + +#include <QtGui/private/qapplication_p.h> +#include <QtCore/QBuffer> +#include <QtCore/QLibrary> + +#include <QDebug> + +#ifndef QT_NO_XFIXES +#include <X11/extensions/Xfixes.h> +#endif // QT_NO_XFIXES + +static const char * x11_atomnames = { + // window-manager <-> client protocols + "WM_PROTOCOLS\0" + "WM_DELETE_WINDOW\0" + "WM_TAKE_FOCUS\0" + "_NET_WM_PING\0" + "_NET_WM_CONTEXT_HELP\0" + "_NET_WM_SYNC_REQUEST\0" + "_NET_WM_SYNC_REQUEST_COUNTER\0" + + // ICCCM window state + "WM_STATE\0" + "WM_CHANGE_STATE\0" + + // Session management + "WM_CLIENT_LEADER\0" + "WM_WINDOW_ROLE\0" + "SM_CLIENT_ID\0" + + // Clipboard + "CLIPBOARD\0" + "INCR\0" + "TARGETS\0" + "MULTIPLE\0" + "TIMESTAMP\0" + "SAVE_TARGETS\0" + "CLIP_TEMPORARY\0" + "_QT_SELECTION\0" + "_QT_CLIPBOARD_SENTINEL\0" + "_QT_SELECTION_SENTINEL\0" + "CLIPBOARD_MANAGER\0" + + "RESOURCE_MANAGER\0" + + "_XSETROOT_ID\0" + + "_QT_SCROLL_DONE\0" + "_QT_INPUT_ENCODING\0" + + "_MOTIF_WM_HINTS\0" + + "DTWM_IS_RUNNING\0" + "ENLIGHTENMENT_DESKTOP\0" + "_DT_SAVE_MODE\0" + "_SGI_DESKS_MANAGER\0" + + // EWMH (aka NETWM) + "_NET_SUPPORTED\0" + "_NET_VIRTUAL_ROOTS\0" + "_NET_WORKAREA\0" + + "_NET_MOVERESIZE_WINDOW\0" + "_NET_WM_MOVERESIZE\0" + + "_NET_WM_NAME\0" + "_NET_WM_ICON_NAME\0" + "_NET_WM_ICON\0" + + "_NET_WM_PID\0" + + "_NET_WM_WINDOW_OPACITY\0" + + "_NET_WM_STATE\0" + "_NET_WM_STATE_ABOVE\0" + "_NET_WM_STATE_BELOW\0" + "_NET_WM_STATE_FULLSCREEN\0" + "_NET_WM_STATE_MAXIMIZED_HORZ\0" + "_NET_WM_STATE_MAXIMIZED_VERT\0" + "_NET_WM_STATE_MODAL\0" + "_NET_WM_STATE_STAYS_ON_TOP\0" + "_NET_WM_STATE_DEMANDS_ATTENTION\0" + + "_NET_WM_USER_TIME\0" + "_NET_WM_USER_TIME_WINDOW\0" + "_NET_WM_FULL_PLACEMENT\0" + + "_NET_WM_WINDOW_TYPE\0" + "_NET_WM_WINDOW_TYPE_DESKTOP\0" + "_NET_WM_WINDOW_TYPE_DOCK\0" + "_NET_WM_WINDOW_TYPE_TOOLBAR\0" + "_NET_WM_WINDOW_TYPE_MENU\0" + "_NET_WM_WINDOW_TYPE_UTILITY\0" + "_NET_WM_WINDOW_TYPE_SPLASH\0" + "_NET_WM_WINDOW_TYPE_DIALOG\0" + "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0" + "_NET_WM_WINDOW_TYPE_POPUP_MENU\0" + "_NET_WM_WINDOW_TYPE_TOOLTIP\0" + "_NET_WM_WINDOW_TYPE_NOTIFICATION\0" + "_NET_WM_WINDOW_TYPE_COMBO\0" + "_NET_WM_WINDOW_TYPE_DND\0" + "_NET_WM_WINDOW_TYPE_NORMAL\0" + "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0" + + "_KDE_NET_WM_FRAME_STRUT\0" + + "_NET_STARTUP_INFO\0" + "_NET_STARTUP_INFO_BEGIN\0" + + "_NET_SUPPORTING_WM_CHECK\0" + + "_NET_WM_CM_S0\0" + + "_NET_SYSTEM_TRAY_VISUAL\0" + + "_NET_ACTIVE_WINDOW\0" + + // Property formats + "COMPOUND_TEXT\0" + "TEXT\0" + "UTF8_STRING\0" + + // xdnd + "XdndEnter\0" + "XdndPosition\0" + "XdndStatus\0" + "XdndLeave\0" + "XdndDrop\0" + "XdndFinished\0" + "XdndTypeList\0" + "XdndActionList\0" + + "XdndSelection\0" + + "XdndAware\0" + "XdndProxy\0" + + "XdndActionCopy\0" + "XdndActionLink\0" + "XdndActionMove\0" + "XdndActionPrivate\0" + + // Motif DND + "_MOTIF_DRAG_AND_DROP_MESSAGE\0" + "_MOTIF_DRAG_INITIATOR_INFO\0" + "_MOTIF_DRAG_RECEIVER_INFO\0" + "_MOTIF_DRAG_WINDOW\0" + "_MOTIF_DRAG_TARGETS\0" + + "XmTRANSFER_SUCCESS\0" + "XmTRANSFER_FAILURE\0" + + // Xkb + "_XKB_RULES_NAMES\0" + + // XEMBED + "_XEMBED\0" + "_XEMBED_INFO\0" + + // Wacom old. (before version 0.10) + "Wacom Stylus\0" + "Wacom Cursor\0" + "Wacom Eraser\0" + + // Tablet + "STYLUS\0" + "ERASER\0" +}; + +/*! + \internal + Try to resolve a \a symbol from \a library with the version specified + by \a vernum. + + Note that, in the case of the Xfixes library, \a vernum is not the same as + \c XFIXES_MAJOR - it is a part of soname and may differ from the Xfixes + version. +*/ +static void* qt_load_library_runtime(const char *library, int vernum, + int highestVernum, const char *symbol) +{ + QList<int> versions; + // we try to load in the following order: + // explicit version -> the default one -> (from the highest (highestVernum) to the lowest (vernum) ) + if (vernum != -1) + versions << vernum; + versions << -1; + if (vernum != -1) { + for(int i = highestVernum; i > vernum; --i) + versions << i; + } + Q_FOREACH(int version, versions) { + QLatin1String libName(library); + QLibrary xfixesLib(libName, version); + void *ptr = xfixesLib.resolve(symbol); + if (ptr) + return ptr; + } + return 0; +} + +# define XFIXES_LOAD_RUNTIME(vernum, symbol, symbol_type) \ + (symbol_type)qt_load_library_runtime("libXfixes", vernum, 4, #symbol); +# define XFIXES_LOAD_V1(symbol) \ + XFIXES_LOAD_RUNTIME(1, symbol, Ptr##symbol) +# define XFIXES_LOAD_V2(symbol) \ + XFIXES_LOAD_RUNTIME(2, symbol, Ptr##symbol) + + +class QTestLiteStaticInfoPrivate +{ +public: + QTestLiteStaticInfoPrivate() + : use_xfixes(false) + , xfixes_major(0) + , xfixes_eventbase(0) + , xfixes_errorbase(0) + { + QTestLiteScreen *screen = qobject_cast<QTestLiteScreen *> (QApplicationPrivate::platformIntegration()->screens().at(0)); + Q_ASSERT(screen); + + initializeAllAtoms(screen); + initializeSupportedAtoms(screen); + + resolveXFixes(screen); + } + + bool isSupportedByWM(Atom atom) + { + if (!m_supportedAtoms) + return false; + + bool supported = false; + int i = 0; + while (m_supportedAtoms[i] != 0) { + if (m_supportedAtoms[i++] == atom) { + supported = true; + break; + } + } + + return supported; + } + + Atom atom(QTestLiteStatic::X11Atom atom) + { + return m_allAtoms[atom]; + } + + bool useXFixes() const { return use_xfixes; } + + int xFixesEventBase() const {return xfixes_eventbase; } + + PtrXFixesSelectSelectionInput xFixesSelectSelectionInput() const + { + return ptrXFixesSelectSelectionInput; + } + + QImage qimageFromXImage(XImage *xi) + { + QImage::Format format = QImage::Format_ARGB32_Premultiplied; + if (xi->depth == 24) + format = QImage::Format_RGB32; + else if (xi->depth == 16) + format = QImage::Format_RGB16; + + QImage image = QImage((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format).copy(); + + // we may have to swap the byte order + if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst) + || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst)) + { + for (int i=0; i < image.height(); i++) { + if (xi->depth == 16) { + ushort *p = (ushort*)image.scanLine(i); + ushort *end = p + image.width(); + while (p < end) { + *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff); + p++; + } + } else { + uint *p = (uint*)image.scanLine(i); + uint *end = p + image.width(); + while (p < end) { + *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000) + | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff); + p++; + } + } + } + } + + // fix-up alpha channel + if (format == QImage::Format_RGB32) { + QRgb *p = (QRgb *)image.bits(); + for (int y = 0; y < xi->height; ++y) { + for (int x = 0; x < xi->width; ++x) + p[x] |= 0xff000000; + p += xi->bytes_per_line / 4; + } + } + + return image; + } + + +private: + + void initializeAllAtoms(QTestLiteScreen *screen) { + const char *names[QTestLiteStatic::NAtoms]; + const char *ptr = x11_atomnames; + + int i = 0; + while (*ptr) { + names[i++] = ptr; + while (*ptr) + ++ptr; + ++ptr; + } + + Q_ASSERT(i == QTestLiteStatic::NPredefinedAtoms); + + QByteArray settings_atom_name("_QT_SETTINGS_TIMESTAMP_"); + settings_atom_name += XDisplayName(qPrintable(screen->displayName())); + names[i++] = settings_atom_name; + + Q_ASSERT(i == QTestLiteStatic::NAtoms); + #if 0//defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 6) + XInternAtoms(screen->display(), (char **)names, i, False, m_allAtoms); + #else + for (i = 0; i < QTestLiteStatic::NAtoms; ++i) + m_allAtoms[i] = XInternAtom(screen->display(), (char *)names[i], False); + #endif + } + + void initializeSupportedAtoms(QTestLiteScreen *screen) + { + Atom type; + int format; + long offset = 0; + unsigned long nitems, after; + unsigned char *data = 0; + + int e = XGetWindowProperty(screen->display(), screen->rootWindow(), + this->atom(QTestLiteStatic::_NET_SUPPORTED), 0, 0, + False, XA_ATOM, &type, &format, &nitems, &after, &data); + if (data) + XFree(data); + + if (e == Success && type == XA_ATOM && format == 32) { + QBuffer ts; + ts.open(QIODevice::WriteOnly); + + while (after > 0) { + XGetWindowProperty(screen->display(), screen->rootWindow(), + this->atom(QTestLiteStatic::_NET_SUPPORTED), offset, 1024, + False, XA_ATOM, &type, &format, &nitems, &after, &data); + + if (type == XA_ATOM && format == 32) { + ts.write(reinterpret_cast<char *>(data), nitems * sizeof(long)); + offset += nitems; + } else + after = 0; + if (data) + XFree(data); + } + + // compute nitems + QByteArray buffer(ts.buffer()); + nitems = buffer.size() / sizeof(Atom); + m_supportedAtoms = new Atom[nitems + 1]; + Atom *a = (Atom *) buffer.data(); + uint i; + for (i = 0; i < nitems; i++) + m_supportedAtoms[i] = a[i]; + m_supportedAtoms[nitems] = 0; + + } + } + + void resolveXFixes(QTestLiteScreen *screen) + { +#ifndef QT_NO_XFIXES + // See if Xfixes is supported on the connected display + if (XQueryExtension(screen->display(), "XFIXES", &xfixes_major, + &xfixes_eventbase, &xfixes_errorbase)) { + ptrXFixesQueryExtension = XFIXES_LOAD_V1(XFixesQueryExtension); + ptrXFixesQueryVersion = XFIXES_LOAD_V1(XFixesQueryVersion); + ptrXFixesSetCursorName = XFIXES_LOAD_V2(XFixesSetCursorName); + ptrXFixesSelectSelectionInput = XFIXES_LOAD_V2(XFixesSelectSelectionInput); + + if(ptrXFixesQueryExtension && ptrXFixesQueryVersion + && ptrXFixesQueryExtension(screen->display(), &xfixes_eventbase, + &xfixes_errorbase)) { + // Xfixes is supported. + // Note: the XFixes protocol version is negotiated using QueryVersion. + // We supply the highest version we support, the X server replies with + // the highest version it supports, but no higher than the version we + // asked for. The version sent back is the protocol version the X server + // will use to talk us. If this call is removed, the behavior of the + // X server when it receives an XFixes request is undefined. + int major = 3; + int minor = 0; + ptrXFixesQueryVersion(screen->display(), &major, &minor); + use_xfixes = (major >= 1); + xfixes_major = major; + } + } +#endif // QT_NO_XFIXES + + } + + Atom *m_supportedAtoms; + Atom m_allAtoms[QTestLiteStatic::NAtoms]; + +#ifndef QT_NO_XFIXES + PtrXFixesQueryExtension ptrXFixesQueryExtension; + PtrXFixesQueryVersion ptrXFixesQueryVersion; + PtrXFixesSetCursorName ptrXFixesSetCursorName; + PtrXFixesSelectSelectionInput ptrXFixesSelectSelectionInput; +#endif + + bool use_xfixes; + int xfixes_major; + int xfixes_eventbase; + int xfixes_errorbase; + +}; +Q_GLOBAL_STATIC(QTestLiteStaticInfoPrivate, qTestLiteStaticInfoPrivate); + + +Atom QTestLiteStatic::atom(QTestLiteStatic::X11Atom atom) +{ + return qTestLiteStaticInfoPrivate()->atom(atom); +} + +bool QTestLiteStatic::isSupportedByWM(Atom atom) +{ + return qTestLiteStaticInfoPrivate()->isSupportedByWM(atom); +} + +bool QTestLiteStatic::useXFixes() +{ + return qTestLiteStaticInfoPrivate()->useXFixes(); +} + +int QTestLiteStatic::xFixesEventBase() +{ + return qTestLiteStaticInfoPrivate()->xFixesEventBase(); +} + +#ifndef QT_NO_XFIXES +PtrXFixesSelectSelectionInput QTestLiteStatic::xFixesSelectSelectionInput() +{ + qDebug() << qTestLiteStaticInfoPrivate()->useXFixes(); + if (!qTestLiteStaticInfoPrivate()->useXFixes()) + return 0; + + return qTestLiteStaticInfoPrivate()->xFixesSelectSelectionInput(); +} + +QImage QTestLiteStatic::qimageFromXImage(XImage *xi) +{ + return qTestLiteStaticInfoPrivate()->qimageFromXImage(xi); +} +#endif //QT_NO_XFIXES diff --git a/src/plugins/platforms/testlite/qtestlitestaticinfo.h b/src/plugins/platforms/testlite/qtestlitestaticinfo.h new file mode 100644 index 0000000..0876768 --- /dev/null +++ b/src/plugins/platforms/testlite/qtestlitestaticinfo.h @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTLITESTATICINFO_H +#define QTESTLITESTATICINFO_H + +#include <QtCore/QTextStream> +#include <QtCore/QDataStream> +#include <QtCore/QMetaType> +#include <QtCore/QVariant> + +#if defined(_XLIB_H_) // crude hack, but... +#error "cannot include <X11/Xlib.h> before this file" +#endif +#define XRegisterIMInstantiateCallback qt_XRegisterIMInstantiateCallback +#define XUnregisterIMInstantiateCallback qt_XUnregisterIMInstantiateCallback +#define XSetIMValues qt_XSetIMValues +#include <X11/Xlib.h> +#undef XRegisterIMInstantiateCallback +#undef XUnregisterIMInstantiateCallback +#undef XSetIMValues + +#include <X11/Xutil.h> +#include <X11/Xos.h> +#ifdef index +# undef index +#endif +#ifdef rindex +# undef rindex +#endif +#ifdef Q_OS_VXWORS +# ifdef open +# undef open +# endif +# ifdef getpid +# undef getpid +# endif +#endif // Q_OS_VXWORKS +#include <X11/Xatom.h> + +//#define QT_NO_SHAPE +#ifdef QT_NO_SHAPE +# define XShapeCombineRegion(a,b,c,d,e,f,g) +# define XShapeCombineMask(a,b,c,d,e,f,g) +#else +# include <X11/extensions/shape.h> +#endif // QT_NO_SHAPE + + +#if !defined (QT_NO_TABLET) +# include <X11/extensions/XInput.h> +#if defined (Q_OS_IRIX) +# include <X11/extensions/SGIMisc.h> +# include <wacom.h> +#endif +#endif // QT_NO_TABLET + + +// #define QT_NO_XINERAMA +#ifndef QT_NO_XINERAMA +// XFree86 does not C++ify Xinerama (at least up to XFree86 4.0.3). +extern "C" { +# include <X11/extensions/Xinerama.h> +} +#endif // QT_NO_XINERAMA + +// #define QT_NO_XRANDR +#ifndef QT_NO_XRANDR +# include <X11/extensions/Xrandr.h> +#endif // QT_NO_XRANDR + +// #define QT_NO_XRENDER +#ifndef QT_NO_XRENDER +# include <X11/extensions/Xrender.h> +#endif // QT_NO_XRENDER + +#ifndef QT_NO_XSYNC +extern "C" { +# include "X11/extensions/sync.h" +} +#endif + +// #define QT_NO_XKB +#ifndef QT_NO_XKB +# include <X11/XKBlib.h> +#endif // QT_NO_XKB + + +#if !defined(XlibSpecificationRelease) +# define X11R4 +typedef char *XPointer; +#else +# undef X11R4 +#endif + +#ifndef QT_NO_XFIXES +typedef Bool (*PtrXFixesQueryExtension)(Display *, int *, int *); +typedef Status (*PtrXFixesQueryVersion)(Display *, int *, int *); +typedef void (*PtrXFixesSetCursorName)(Display *dpy, Cursor cursor, const char *name); +typedef void (*PtrXFixesSelectSelectionInput)(Display *dpy, Window win, Atom selection, unsigned long eventMask); +#endif // QT_NO_XFIXES + +#ifndef QT_NO_XCURSOR +#include <X11/Xcursor/Xcursor.h> +typedef Cursor (*PtrXcursorLibraryLoadCursor)(Display *, const char *); +#endif // QT_NO_XCURSOR + +#ifndef QT_NO_XINERAMA +typedef Bool (*PtrXineramaQueryExtension)(Display *dpy, int *event_base, int *error_base); +typedef Bool (*PtrXineramaIsActive)(Display *dpy); +typedef XineramaScreenInfo *(*PtrXineramaQueryScreens)(Display *dpy, int *number); +#endif // QT_NO_XINERAMA + +#ifndef QT_NO_XRANDR +typedef void (*PtrXRRSelectInput)(Display *, Window, int); +typedef int (*PtrXRRUpdateConfiguration)(XEvent *); +typedef int (*PtrXRRRootToScreen)(Display *, Window); +typedef Bool (*PtrXRRQueryExtension)(Display *, int *, int *); +#endif // QT_NO_XRANDR + +#ifndef QT_NO_XINPUT +typedef int (*PtrXCloseDevice)(Display *, XDevice *); +typedef XDeviceInfo* (*PtrXListInputDevices)(Display *, int *); +typedef XDevice* (*PtrXOpenDevice)(Display *, XID); +typedef void (*PtrXFreeDeviceList)(XDeviceInfo *); +typedef int (*PtrXSelectExtensionEvent)(Display *, Window, XEventClass *, int); +#endif // QT_NO_XINPUT + +/* + * Solaris patch 108652-47 and higher fixes crases in + * XRegisterIMInstantiateCallback, but the function doesn't seem to + * work. + * + * Instead, we disabled R6 input, and open the input method + * immediately at application start. + */ + +//######### XFree86 has wrong declarations for XRegisterIMInstantiateCallback +//######### and XUnregisterIMInstantiateCallback in at least version 3.3.2. +//######### Many old X11R6 header files lack XSetIMValues. +//######### Therefore, we have to declare these functions ourselves. + +extern "C" Bool XRegisterIMInstantiateCallback( + Display*, + struct _XrmHashBucketRec*, + char*, + char*, + XIMProc, //XFree86 has XIDProc, which has to be wrong + XPointer +); + +extern "C" Bool XUnregisterIMInstantiateCallback( + Display*, + struct _XrmHashBucketRec*, + char*, + char*, + XIMProc, //XFree86 has XIDProc, which has to be wrong + XPointer +); + +#ifndef X11R4 +# include <X11/Xlocale.h> +#endif // X11R4 + + +#ifndef QT_NO_MITSHM +# include <X11/extensions/XShm.h> +#endif // QT_NO_MITSHM + +// rename a couple of X defines to get rid of name clashes +// resolve the conflict between X11's FocusIn and QEvent::FocusIn +enum { + XFocusOut = FocusOut, + XFocusIn = FocusIn, + XKeyPress = KeyPress, + XKeyRelease = KeyRelease, + XNone = None, + XRevertToParent = RevertToParent, + XGrayScale = GrayScale, + XCursorShape = CursorShape +}; +#undef FocusOut +#undef FocusIn +#undef KeyPress +#undef KeyRelease +#undef None +#undef RevertToParent +#undef GrayScale +#undef CursorShape + +#ifdef FontChange +#undef FontChange +#endif + + +class QTestLiteStatic +{ +public: + enum X11Atom { + // window-manager <-> client protocols + WM_PROTOCOLS, + WM_DELETE_WINDOW, + WM_TAKE_FOCUS, + _NET_WM_PING, + _NET_WM_CONTEXT_HELP, + _NET_WM_SYNC_REQUEST, + _NET_WM_SYNC_REQUEST_COUNTER, + + // ICCCM window state + WM_STATE, + WM_CHANGE_STATE, + + // Session management + WM_CLIENT_LEADER, + WM_WINDOW_ROLE, + SM_CLIENT_ID, + + // Clipboard + CLIPBOARD, + INCR, + TARGETS, + MULTIPLE, + TIMESTAMP, + SAVE_TARGETS, + CLIP_TEMPORARY, + _QT_SELECTION, + _QT_CLIPBOARD_SENTINEL, + _QT_SELECTION_SENTINEL, + CLIPBOARD_MANAGER, + + RESOURCE_MANAGER, + + _XSETROOT_ID, + + _QT_SCROLL_DONE, + _QT_INPUT_ENCODING, + + _MOTIF_WM_HINTS, + + DTWM_IS_RUNNING, + ENLIGHTENMENT_DESKTOP, + _DT_SAVE_MODE, + _SGI_DESKS_MANAGER, + + // EWMH (aka NETWM) + _NET_SUPPORTED, + _NET_VIRTUAL_ROOTS, + _NET_WORKAREA, + + _NET_MOVERESIZE_WINDOW, + _NET_WM_MOVERESIZE, + + _NET_WM_NAME, + _NET_WM_ICON_NAME, + _NET_WM_ICON, + + _NET_WM_PID, + + _NET_WM_WINDOW_OPACITY, + + _NET_WM_STATE, + _NET_WM_STATE_ABOVE, + _NET_WM_STATE_BELOW, + _NET_WM_STATE_FULLSCREEN, + _NET_WM_STATE_MAXIMIZED_HORZ, + _NET_WM_STATE_MAXIMIZED_VERT, + _NET_WM_STATE_MODAL, + _NET_WM_STATE_STAYS_ON_TOP, + _NET_WM_STATE_DEMANDS_ATTENTION, + + _NET_WM_USER_TIME, + _NET_WM_USER_TIME_WINDOW, + _NET_WM_FULL_PLACEMENT, + + _NET_WM_WINDOW_TYPE, + _NET_WM_WINDOW_TYPE_DESKTOP, + _NET_WM_WINDOW_TYPE_DOCK, + _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_MENU, + _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_SPLASH, + _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_POPUP_MENU, + _NET_WM_WINDOW_TYPE_TOOLTIP, + _NET_WM_WINDOW_TYPE_NOTIFICATION, + _NET_WM_WINDOW_TYPE_COMBO, + _NET_WM_WINDOW_TYPE_DND, + _NET_WM_WINDOW_TYPE_NORMAL, + _KDE_NET_WM_WINDOW_TYPE_OVERRIDE, + + _KDE_NET_WM_FRAME_STRUT, + + _NET_STARTUP_INFO, + _NET_STARTUP_INFO_BEGIN, + + _NET_SUPPORTING_WM_CHECK, + + _NET_WM_CM_S0, + + _NET_SYSTEM_TRAY_VISUAL, + + _NET_ACTIVE_WINDOW, + + // Property formats + COMPOUND_TEXT, + TEXT, + UTF8_STRING, + + // Xdnd + XdndEnter, + XdndPosition, + XdndStatus, + XdndLeave, + XdndDrop, + XdndFinished, + XdndTypelist, + XdndActionList, + + XdndSelection, + + XdndAware, + XdndProxy, + + XdndActionCopy, + XdndActionLink, + XdndActionMove, + XdndActionPrivate, + + // Motif DND + _MOTIF_DRAG_AND_DROP_MESSAGE, + _MOTIF_DRAG_INITIATOR_INFO, + _MOTIF_DRAG_RECEIVER_INFO, + _MOTIF_DRAG_WINDOW, + _MOTIF_DRAG_TARGETS, + + XmTRANSFER_SUCCESS, + XmTRANSFER_FAILURE, + + // Xkb + _XKB_RULES_NAMES, + + // XEMBED + _XEMBED, + _XEMBED_INFO, + + XWacomStylus, + XWacomCursor, + XWacomEraser, + + XTabletStylus, + XTabletEraser, + + NPredefinedAtoms, + + _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms, + NAtoms + }; + + static Atom atom(X11Atom atom); + static bool isSupportedByWM(Atom atom); + + static bool useXFixes(); + static int xFixesEventBase(); + + #ifndef QT_NO_XFIXES + static PtrXFixesSelectSelectionInput xFixesSelectSelectionInput(); + #endif //QT_NO_XFIXES + + static QImage qimageFromXImage(XImage *xi); + + +}; + +#endif // QTESTLITESTATICINFO_H diff --git a/src/plugins/platforms/testlite/qtestlitewindow.cpp b/src/plugins/platforms/testlite/qtestlitewindow.cpp index b52aae9..d9c69e3 100644 --- a/src/plugins/platforms/testlite/qtestlitewindow.cpp +++ b/src/plugins/platforms/testlite/qtestlitewindow.cpp @@ -39,191 +39,125 @@ ** ****************************************************************************/ -#include "qtestliteintegration.h" -#include <QWindowSystemInterface> -#include <private/qwindowsurface_p.h> -#include <QtGui/private/qapplication_p.h> - #include "qtestlitewindow.h" -#include <QBitmap> -#include <QCursor> -#include <QDateTime> -#include <QPixmap> -#include <QImage> -#include <QSocketNotifier> +#include "qtestliteintegration.h" +#include "qtestlitescreen.h" +#include "qtestlitekeyboard.h" +#include "qtestlitestaticinfo.h" -#include <qdebug.h> -#include <QTimer> +#include <QtGui/QWindowSystemInterface> +#include <QSocketNotifier> #include <QApplication> +#include <QDebug> -#ifndef QT_NO_OPENGL -#include "qglxintegration.h" -#endif - -#include <stdio.h> -#include <stdlib.h> - - -#include <X11/Xatom.h> - -#include <X11/cursorfont.h> - - +#include <QtGui/private/qwindowsurface_p.h> +#include <QtGui/private/qapplication_p.h> -//### remove stuff we don't want from qt_x11_p.h -#undef ATOM -#undef X11 +#if !defined(QT_NO_OPENGL) +#if !defined(QT_OPENGL_ES_2) +#include "qglxintegration.h" +#else +#include "../eglconvenience/qeglconvenience.h" +#include "../eglconvenience/qeglplatformcontext.h" +#include "qtestliteeglintegration.h" +#endif //QT_OPENGL_ES_2 +#endif //QT_NO_OPENGL //#define MYX11_DEBUG QT_BEGIN_NAMESPACE -static int (*original_x_errhandler)(Display *dpy, XErrorEvent *); -static bool seen_badwindow; - - -static Atom wmProtocolsAtom; -static Atom wmDeleteWindowAtom; - -class MyX11CursorNode -{ -public: - MyX11CursorNode(int id, Cursor c) { idValue = id; cursorValue = c; refCount = 1; } - QDateTime expiration() { return t; } - void setExpiration(QDateTime val) { t = val; } - MyX11CursorNode * ante() { return before; } - void setAnte(MyX11CursorNode *node) { before = node; } - MyX11CursorNode * post() { return after; } - void setPost(MyX11CursorNode *node) { after = node; } - Cursor cursor() { return cursorValue; } - int id() { return idValue; } - unsigned int refCount; - -private: - MyX11CursorNode *before; - MyX11CursorNode *after; - QDateTime t; - Cursor cursorValue; - int idValue; - - Display * display; -}; - - - - - -class MyX11Cursors : public QObject +QTestLiteWindow::QTestLiteWindow(QWidget *window) + : QPlatformWindow(window) + , mGLContext(0) + , mScreen(QTestLiteScreen::testLiteScreenForWidget(window)) { - Q_OBJECT -public: - MyX11Cursors(Display * d); - ~MyX11Cursors() { timer.stop(); } - void incrementUseCount(int id); - void decrementUseCount(int id); - void createNode(int id, Cursor c); - bool exists(int id) { return lookupMap.contains(id); } - Cursor cursor(int id); -public slots: - void timeout(); - -private: - void removeNode(MyX11CursorNode *node); - void insertNode(MyX11CursorNode *node); - - // linked list of cursors currently not assigned to any window - MyX11CursorNode *firstExpired; - MyX11CursorNode *lastExpired; - - QHash<int, MyX11CursorNode *> lookupMap; - QTimer timer; - - Display *display; - - int removalDelay; -}; - - - - - -QTestLiteWindow::QTestLiteWindow(const QTestLiteIntegration *platformIntegration, - QTestLiteScreen */*screen*/, QWidget *window) - :QPlatformWindow(window), mGLContext(0) -{ - xd = platformIntegration->xd; - xd->windowList.append(this); - { - int x = window->x(); - int y = window->y(); - int w = window->width(); - int h = window->height(); + int x = window->x(); + int y = window->y(); + int w = window->width(); + int h = window->height(); if(window->platformWindowFormat().windowApi() == QPlatformWindowFormat::OpenGL && QApplicationPrivate::platformIntegration()->hasOpenGL() ) { -#ifndef QT_NO_OPENGL - XVisualInfo *visualInfo = QGLXGLContext::findVisualInfo(xd,window->platformWindowFormat()); - Colormap cmap = XCreateColormap(xd->display,xd->rootWindow(),visualInfo->visual,AllocNone); - - XSetWindowAttributes a; - a.colormap = cmap; - x_window = XCreateWindow(xd->display, xd->rootWindow(),x, y, w, h, - 0, visualInfo->depth, InputOutput, visualInfo->visual, - CWColormap, &a); -#endif //QT_NO_OPENGL + #if !defined(QT_NO_OPENGL) +#if !defined(QT_OPENGL_ES_2) + XVisualInfo *visualInfo = QGLXContext::findVisualInfo(mScreen,window->platformWindowFormat()); +#else + QPlatformWindowFormat windowFormat = correctColorBuffers(window->platformWindowFormat()); + + EGLDisplay eglDisplay = eglGetDisplay(mScreen->display()); + EGLConfig eglConfig = q_configFromQPlatformWindowFormat(eglDisplay,windowFormat); + VisualID id = QTestLiteEglIntegration::getCompatibleVisualId(mScreen->display(),eglConfig); + + XVisualInfo visualInfoTemplate; + memset(&visualInfoTemplate, 0, sizeof(XVisualInfo)); + visualInfoTemplate.visualid = id; + + XVisualInfo *visualInfo; + int matchingCount = 0; + visualInfo = XGetVisualInfo(mScreen->display(), VisualIDMask, &visualInfoTemplate, &matchingCount); +#endif //!defined(QT_OPENGL_ES_2) + if (visualInfo) { + Colormap cmap = XCreateColormap(mScreen->display(),mScreen->rootWindow(),visualInfo->visual,AllocNone); + + XSetWindowAttributes a; + a.colormap = cmap; + x_window = XCreateWindow(mScreen->display(), mScreen->rootWindow(),x, y, w, h, + 0, visualInfo->depth, InputOutput, visualInfo->visual, + CWColormap, &a); + } else { + qFatal("no window!"); + } +#endif //!defined(QT_NO_OPENGL) } else { - x_window = XCreateSimpleWindow(xd->display, xd->rootWindow(), + x_window = XCreateSimpleWindow(mScreen->display(), mScreen->rootWindow(), x, y, w, h, 0 /*border_width*/, - xd->blackPixel(), xd->whitePixel()); + mScreen->blackPixel(), mScreen->whitePixel()); } #ifdef MYX11_DEBUG qDebug() << "QTestLiteWindow::QTestLiteWindow creating" << hex << x_window << window; #endif - } - - width = -1; - height = -1; - xpos = -1; - ypos = -1; - XSetWindowBackgroundPixmap(xd->display, x_window, XNone); + XSetWindowBackgroundPixmap(mScreen->display(), x_window, XNone); - XSelectInput(xd->display, x_window, ExposureMask | KeyPressMask | KeyReleaseMask | + XSelectInput(mScreen->display(), x_window, + ExposureMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | - PointerMotionMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | PropertyChangeMask | StructureNotifyMask); gc = createGC(); - XChangeProperty (xd->display, x_window, - wmProtocolsAtom, - XA_ATOM, 32, PropModeAppend, - (unsigned char *) &wmDeleteWindowAtom, 1); - currentCursor = -1; + Atom protocols[5]; + int n = 0; + protocols[n++] = QTestLiteStatic::atom(QTestLiteStatic::WM_DELETE_WINDOW); // support del window protocol + protocols[n++] = QTestLiteStatic::atom(QTestLiteStatic::WM_TAKE_FOCUS); // support take focus window protocol + protocols[n++] = QTestLiteStatic::atom(QTestLiteStatic::_NET_WM_PING); // support _NET_WM_PING protocol +#ifndef QT_NO_XSYNC + protocols[n++] = QTestLiteStatic::atom(QTestLiteStatic::_NET_WM_SYNC_REQUEST); // support _NET_WM_SYNC_REQUEST protocol +#endif // QT_NO_XSYNC + if (window->windowFlags() & Qt::WindowContextHelpButtonHint) + protocols[n++] = QTestLiteStatic::atom(QTestLiteStatic::_NET_WM_CONTEXT_HELP); + XSetWMProtocols(mScreen->display(), x_window, protocols, n); } + QTestLiteWindow::~QTestLiteWindow() { #ifdef MYX11_DEBUG qDebug() << "~QTestLiteWindow" << hex << x_window; #endif delete mGLContext; - XFreeGC(xd->display, gc); - XDestroyWindow(xd->display, x_window); - - xd->windowList.removeAll(this); + XFreeGC(mScreen->display(), gc); + XDestroyWindow(mScreen->display(), x_window); } - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Mouse event stuff - - - - static Qt::MouseButtons translateMouseButtons(int s) { Qt::MouseButtons ret = 0; @@ -237,27 +171,6 @@ static Qt::MouseButtons translateMouseButtons(int s) } -static Qt::KeyboardModifiers translateModifiers(int s) -{ - const uchar qt_alt_mask = Mod1Mask; - const uchar qt_meta_mask = Mod4Mask; - - - Qt::KeyboardModifiers ret = 0; - if (s & ShiftMask) - ret |= Qt::ShiftModifier; - if (s & ControlMask) - ret |= Qt::ControlModifier; - if (s & qt_alt_mask) - ret |= Qt::AltModifier; - if (s & qt_meta_mask) - ret |= Qt::MetaModifier; -#if 0 - if (s & qt_mode_switch_mask) - ret |= Qt::GroupSwitchModifier; -#endif - return ret; -} void QTestLiteWindow::handleMouseEvent(QEvent::Type type, XButtonEvent *e) { @@ -265,7 +178,7 @@ void QTestLiteWindow::handleMouseEvent(QEvent::Type type, XButtonEvent *e) Qt::MouseButton button = Qt::NoButton; Qt::MouseButtons buttons = translateMouseButtons(e->state); - Qt::KeyboardModifiers modifiers = translateModifiers(e->state); + Qt::KeyboardModifiers modifiers = mScreen->keyboard()->translateModifiers(e->state); if (type != QEvent::MouseMove) { switch (e->button) { case Button1: button = Qt::LeftButton; break; @@ -318,319 +231,21 @@ void QTestLiteWindow::handleLeaveEvent() QWindowSystemInterface::handleLeaveEvent(widget()); } - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Key event stuff -- not pretty either -// -// What we want to do is to port Robert's keytable code properly - -// keyboard mapping table -static const unsigned int keyTbl[] = { - - // misc keys - - XK_Escape, Qt::Key_Escape, - XK_Tab, Qt::Key_Tab, - XK_ISO_Left_Tab, Qt::Key_Backtab, - XK_BackSpace, Qt::Key_Backspace, - XK_Return, Qt::Key_Return, - XK_Insert, Qt::Key_Insert, - XK_Delete, Qt::Key_Delete, - XK_Clear, Qt::Key_Delete, - XK_Pause, Qt::Key_Pause, - XK_Print, Qt::Key_Print, - 0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq - 0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq - - // cursor movement - - XK_Home, Qt::Key_Home, - XK_End, Qt::Key_End, - XK_Left, Qt::Key_Left, - XK_Up, Qt::Key_Up, - XK_Right, Qt::Key_Right, - XK_Down, Qt::Key_Down, - XK_Prior, Qt::Key_PageUp, - XK_Next, Qt::Key_PageDown, - - // modifiers - - XK_Shift_L, Qt::Key_Shift, - XK_Shift_R, Qt::Key_Shift, - XK_Shift_Lock, Qt::Key_Shift, - XK_Control_L, Qt::Key_Control, - XK_Control_R, Qt::Key_Control, - XK_Meta_L, Qt::Key_Meta, - XK_Meta_R, Qt::Key_Meta, - XK_Alt_L, Qt::Key_Alt, - XK_Alt_R, Qt::Key_Alt, - XK_Caps_Lock, Qt::Key_CapsLock, - XK_Num_Lock, Qt::Key_NumLock, - XK_Scroll_Lock, Qt::Key_ScrollLock, - XK_Super_L, Qt::Key_Super_L, - XK_Super_R, Qt::Key_Super_R, - XK_Menu, Qt::Key_Menu, - XK_Hyper_L, Qt::Key_Hyper_L, - XK_Hyper_R, Qt::Key_Hyper_R, - XK_Help, Qt::Key_Help, - 0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab - 0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11) - 0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12) - - // numeric and function keypad keys - - XK_KP_Space, Qt::Key_Space, - XK_KP_Tab, Qt::Key_Tab, - XK_KP_Enter, Qt::Key_Enter, - //XK_KP_F1, Qt::Key_F1, - //XK_KP_F2, Qt::Key_F2, - //XK_KP_F3, Qt::Key_F3, - //XK_KP_F4, Qt::Key_F4, - XK_KP_Home, Qt::Key_Home, - XK_KP_Left, Qt::Key_Left, - XK_KP_Up, Qt::Key_Up, - XK_KP_Right, Qt::Key_Right, - XK_KP_Down, Qt::Key_Down, - XK_KP_Prior, Qt::Key_PageUp, - XK_KP_Next, Qt::Key_PageDown, - XK_KP_End, Qt::Key_End, - XK_KP_Begin, Qt::Key_Clear, - XK_KP_Insert, Qt::Key_Insert, - XK_KP_Delete, Qt::Key_Delete, - XK_KP_Equal, Qt::Key_Equal, - XK_KP_Multiply, Qt::Key_Asterisk, - XK_KP_Add, Qt::Key_Plus, - XK_KP_Separator, Qt::Key_Comma, - XK_KP_Subtract, Qt::Key_Minus, - XK_KP_Decimal, Qt::Key_Period, - XK_KP_Divide, Qt::Key_Slash, - - // International input method support keys - - // International & multi-key character composition - XK_ISO_Level3_Shift, Qt::Key_AltGr, - XK_Multi_key, Qt::Key_Multi_key, - XK_Codeinput, Qt::Key_Codeinput, - XK_SingleCandidate, Qt::Key_SingleCandidate, - XK_MultipleCandidate, Qt::Key_MultipleCandidate, - XK_PreviousCandidate, Qt::Key_PreviousCandidate, - - // Misc Functions - XK_Mode_switch, Qt::Key_Mode_switch, - XK_script_switch, Qt::Key_Mode_switch, - - // Japanese keyboard support - XK_Kanji, Qt::Key_Kanji, - XK_Muhenkan, Qt::Key_Muhenkan, - //XK_Henkan_Mode, Qt::Key_Henkan_Mode, - XK_Henkan_Mode, Qt::Key_Henkan, - XK_Henkan, Qt::Key_Henkan, - XK_Romaji, Qt::Key_Romaji, - XK_Hiragana, Qt::Key_Hiragana, - XK_Katakana, Qt::Key_Katakana, - XK_Hiragana_Katakana, Qt::Key_Hiragana_Katakana, - XK_Zenkaku, Qt::Key_Zenkaku, - XK_Hankaku, Qt::Key_Hankaku, - XK_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku, - XK_Touroku, Qt::Key_Touroku, - XK_Massyo, Qt::Key_Massyo, - XK_Kana_Lock, Qt::Key_Kana_Lock, - XK_Kana_Shift, Qt::Key_Kana_Shift, - XK_Eisu_Shift, Qt::Key_Eisu_Shift, - XK_Eisu_toggle, Qt::Key_Eisu_toggle, - //XK_Kanji_Bangou, Qt::Key_Kanji_Bangou, - //XK_Zen_Koho, Qt::Key_Zen_Koho, - //XK_Mae_Koho, Qt::Key_Mae_Koho, - XK_Kanji_Bangou, Qt::Key_Codeinput, - XK_Zen_Koho, Qt::Key_MultipleCandidate, - XK_Mae_Koho, Qt::Key_PreviousCandidate, - -#ifdef XK_KOREAN - // Korean keyboard support - XK_Hangul, Qt::Key_Hangul, - XK_Hangul_Start, Qt::Key_Hangul_Start, - XK_Hangul_End, Qt::Key_Hangul_End, - XK_Hangul_Hanja, Qt::Key_Hangul_Hanja, - XK_Hangul_Jamo, Qt::Key_Hangul_Jamo, - XK_Hangul_Romaja, Qt::Key_Hangul_Romaja, - //XK_Hangul_Codeinput, Qt::Key_Hangul_Codeinput, - XK_Hangul_Codeinput, Qt::Key_Codeinput, - XK_Hangul_Jeonja, Qt::Key_Hangul_Jeonja, - XK_Hangul_Banja, Qt::Key_Hangul_Banja, - XK_Hangul_PreHanja, Qt::Key_Hangul_PreHanja, - XK_Hangul_PostHanja, Qt::Key_Hangul_PostHanja, - //XK_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate, - //XK_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate, - //XK_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate, - XK_Hangul_SingleCandidate, Qt::Key_SingleCandidate, - XK_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate, - XK_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate, - XK_Hangul_Special, Qt::Key_Hangul_Special, - //XK_Hangul_switch, Qt::Key_Hangul_switch, - XK_Hangul_switch, Qt::Key_Mode_switch, -#endif // XK_KOREAN - - // dead keys - XK_dead_grave, Qt::Key_Dead_Grave, - XK_dead_acute, Qt::Key_Dead_Acute, - XK_dead_circumflex, Qt::Key_Dead_Circumflex, - XK_dead_tilde, Qt::Key_Dead_Tilde, - XK_dead_macron, Qt::Key_Dead_Macron, - XK_dead_breve, Qt::Key_Dead_Breve, - XK_dead_abovedot, Qt::Key_Dead_Abovedot, - XK_dead_diaeresis, Qt::Key_Dead_Diaeresis, - XK_dead_abovering, Qt::Key_Dead_Abovering, - XK_dead_doubleacute, Qt::Key_Dead_Doubleacute, - XK_dead_caron, Qt::Key_Dead_Caron, - XK_dead_cedilla, Qt::Key_Dead_Cedilla, - XK_dead_ogonek, Qt::Key_Dead_Ogonek, - XK_dead_iota, Qt::Key_Dead_Iota, - XK_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound, - XK_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound, - XK_dead_belowdot, Qt::Key_Dead_Belowdot, - XK_dead_hook, Qt::Key_Dead_Hook, - XK_dead_horn, Qt::Key_Dead_Horn, - -#if 0 - // Special multimedia keys - // currently only tested with MS internet keyboard - - // browsing keys - XF86XK_Back, Qt::Key_Back, - XF86XK_Forward, Qt::Key_Forward, - XF86XK_Stop, Qt::Key_Stop, - XF86XK_Refresh, Qt::Key_Refresh, - XF86XK_Favorites, Qt::Key_Favorites, - XF86XK_AudioMedia, Qt::Key_LaunchMedia, - XF86XK_OpenURL, Qt::Key_OpenUrl, - XF86XK_HomePage, Qt::Key_HomePage, - XF86XK_Search, Qt::Key_Search, - - // media keys - XF86XK_AudioLowerVolume, Qt::Key_VolumeDown, - XF86XK_AudioMute, Qt::Key_VolumeMute, - XF86XK_AudioRaiseVolume, Qt::Key_VolumeUp, - XF86XK_AudioPlay, Qt::Key_MediaPlay, - XF86XK_AudioStop, Qt::Key_MediaStop, - XF86XK_AudioPrev, Qt::Key_MediaPrevious, - XF86XK_AudioNext, Qt::Key_MediaNext, - XF86XK_AudioRecord, Qt::Key_MediaRecord, - - // launch keys - XF86XK_Mail, Qt::Key_LaunchMail, - XF86XK_MyComputer, Qt::Key_Launch0, - XF86XK_Calculator, Qt::Key_Launch1, - XF86XK_Standby, Qt::Key_Standby, - - XF86XK_Launch0, Qt::Key_Launch2, - XF86XK_Launch1, Qt::Key_Launch3, - XF86XK_Launch2, Qt::Key_Launch4, - XF86XK_Launch3, Qt::Key_Launch5, - XF86XK_Launch4, Qt::Key_Launch6, - XF86XK_Launch5, Qt::Key_Launch7, - XF86XK_Launch6, Qt::Key_Launch8, - XF86XK_Launch7, Qt::Key_Launch9, - XF86XK_Launch8, Qt::Key_LaunchA, - XF86XK_Launch9, Qt::Key_LaunchB, - XF86XK_LaunchA, Qt::Key_LaunchC, - XF86XK_LaunchB, Qt::Key_LaunchD, - XF86XK_LaunchC, Qt::Key_LaunchE, - XF86XK_LaunchD, Qt::Key_LaunchF, -#endif - -#if 0 - // Qtopia keys - QTOPIAXK_Select, Qt::Key_Select, - QTOPIAXK_Yes, Qt::Key_Yes, - QTOPIAXK_No, Qt::Key_No, - QTOPIAXK_Cancel, Qt::Key_Cancel, - QTOPIAXK_Printer, Qt::Key_Printer, - QTOPIAXK_Execute, Qt::Key_Execute, - QTOPIAXK_Sleep, Qt::Key_Sleep, - QTOPIAXK_Play, Qt::Key_Play, - QTOPIAXK_Zoom, Qt::Key_Zoom, - QTOPIAXK_Context1, Qt::Key_Context1, - QTOPIAXK_Context2, Qt::Key_Context2, - QTOPIAXK_Context3, Qt::Key_Context3, - QTOPIAXK_Context4, Qt::Key_Context4, - QTOPIAXK_Call, Qt::Key_Call, - QTOPIAXK_Hangup, Qt::Key_Hangup, - QTOPIAXK_Flip, Qt::Key_Flip, -#endif - 0, 0 -}; - - -static int lookupCode(unsigned int xkeycode) +void QTestLiteWindow::handleFocusInEvent() { - if (xkeycode >= XK_F1 && xkeycode <= XK_F35) - return Qt::Key_F1 + (int(xkeycode) - XK_F1); - - const unsigned int *p = keyTbl; - while (*p) { - if (*p == xkeycode) - return *++p; - p += 2; - } - - return 0; + QWindowSystemInterface::handleWindowActivated(widget()); } - -static Qt::KeyboardModifiers modifierFromKeyCode(int qtcode) +void QTestLiteWindow::handleFocusOutEvent() { - switch (qtcode) { - case Qt::Key_Control: - return Qt::ControlModifier; - case Qt::Key_Alt: - return Qt::AltModifier; - case Qt::Key_Shift: - return Qt::ShiftModifier; - case Qt::Key_Meta: - return Qt::MetaModifier; - default: - return Qt::NoModifier; - } + QWindowSystemInterface::handleWindowActivated(0); } -void QTestLiteWindow::handleKeyEvent(QEvent::Type type, void *ev) -{ - XKeyEvent *e = static_cast<XKeyEvent*>(ev); - - KeySym keySym; - QByteArray chars; - chars.resize(513); - - int count = XLookupString(e, chars.data(), chars.size(), &keySym, 0); - Q_UNUSED(count); -// qDebug() << "QTLWS::handleKeyEvent" << count << hex << "XKeysym:" << keySym; -// if (count) -// qDebug() << hex << int(chars[0]) << "String:" << chars; - - Qt::KeyboardModifiers modifiers = translateModifiers(e->state); - - int qtcode = lookupCode(keySym); -// qDebug() << "lookup: " << hex << keySym << qtcode << "mod" << modifiers; - //X11 specifies state *before*, Qt expects state *after* the event - - modifiers ^= modifierFromKeyCode(qtcode); - - if (qtcode) { - QWindowSystemInterface::handleKeyEvent(widget(), e->time, type, qtcode, modifiers); - } else if (chars[0]) { - int qtcode = chars.toUpper()[0]; //Not exactly right... - if (modifiers & Qt::ControlModifier && qtcode < ' ') - qtcode = chars[0] + '@'; - QWindowSystemInterface::handleKeyEvent(0, e->time, type, qtcode, modifiers, QString::fromLatin1(chars)); - } else { - qWarning() << "unknown X keycode" << hex << e->keycode << keySym; - } -} void QTestLiteWindow::setGeometry(const QRect &rect) { - XMoveResizeWindow(xd->display, x_window, rect.x(), rect.y(), rect.width(), rect.height()); + XMoveResizeWindow(mScreen->display(), x_window, rect.x(), rect.y(), rect.width(), rect.height()); QPlatformWindow::setGeometry(rect); } @@ -647,18 +262,18 @@ WId QTestLiteWindow::winId() const void QTestLiteWindow::setParent(const QPlatformWindow *window) { - QPoint point = widget()->mapTo(widget()->nativeParentWidget(),QPoint()); - XReparentWindow(xd->display,x_window,window->winId(),point.x(),point.y()); + QPoint topLeft = geometry().topLeft(); + XReparentWindow(mScreen->display(),x_window,window->winId(),topLeft.x(),topLeft.y()); } void QTestLiteWindow::raise() { - XRaiseWindow(xd->display, x_window); + XRaiseWindow(mScreen->display(), x_window); } void QTestLiteWindow::lower() { - XLowerWindow(xd->display, x_window); + XLowerWindow(mScreen->display(), x_window); } void QTestLiteWindow::setWindowTitle(const QString &title) @@ -670,14 +285,14 @@ void QTestLiteWindow::setWindowTitle(const QString &title) windowName.format = 8; windowName.nitems = ba.length(); - XSetWMName(xd->display, x_window, &windowName); + XSetWMName(mScreen->display(), x_window, &windowName); } GC QTestLiteWindow::createGC() { GC gc; - gc = XCreateGC(xd->display, x_window, 0, 0); + gc = XCreateGC(mScreen->display(), x_window, 0, 0); if (gc < 0) { qWarning("QTestLiteWindow::createGC() could not create GC"); } @@ -691,30 +306,33 @@ void QTestLiteWindow::paintEvent() #endif if (QWindowSurface *surface = widget()->windowSurface()) - surface->flush(widget(), QRect(xpos,ypos,width, height), QPoint()); + surface->flush(widget(), widget()->geometry(), QPoint()); } +void QTestLiteWindow::requestActivateWindow() +{ + XSetInputFocus(mScreen->display(), x_window, XRevertToParent, CurrentTime); +} void QTestLiteWindow::resizeEvent(XConfigureEvent *e) { - if ((e->width != width || e->height != height) && e->x == 0 && e->y == 0) { + int xpos = geometry().x(); + int ypos = geometry().y(); + if ((e->width != geometry().width() || e->height != geometry().height()) && e->x == 0 && e->y == 0) { //qDebug() << "resize with bogus pos" << e->x << e->y << e->width << e->height << "window"<< hex << window; } else { //qDebug() << "geometry change" << e->x << e->y << e->width << e->height << "window"<< hex << window; xpos = e->x; ypos = e->y; } - width = e->width; - height = e->height; - #ifdef MYX11_DEBUG qDebug() << hex << x_window << dec << "ConfigureNotify" << e->x << e->y << e->width << e->height << "geometry" << xpos << ypos << width << height; #endif - QWindowSystemInterface::handleGeometryChange(widget(), QRect(xpos, ypos, width, height)); + QRect newRect(xpos, ypos, e->width, e->height); + QWindowSystemInterface::handleGeometryChange(widget(), newRect); } - void QTestLiteWindow::mousePressEvent(XButtonEvent *e) { static long prevTime = 0; @@ -738,51 +356,7 @@ void QTestLiteWindow::mousePressEvent(XButtonEvent *e) handleMouseEvent(type, e); } - - -// WindowFlag stuff, lots of copied code from qwidget_x11.cpp... - -//We're hacking here... - - -// MWM support -struct QtMWMHints { - ulong flags, functions, decorations; - long input_mode; - ulong status; -}; - -enum { - MWM_HINTS_FUNCTIONS = (1L << 0), - - MWM_FUNC_ALL = (1L << 0), - MWM_FUNC_RESIZE = (1L << 1), - MWM_FUNC_MOVE = (1L << 2), - MWM_FUNC_MINIMIZE = (1L << 3), - MWM_FUNC_MAXIMIZE = (1L << 4), - MWM_FUNC_CLOSE = (1L << 5), - - MWM_HINTS_DECORATIONS = (1L << 1), - - MWM_DECOR_ALL = (1L << 0), - MWM_DECOR_BORDER = (1L << 1), - MWM_DECOR_RESIZEH = (1L << 2), - MWM_DECOR_TITLE = (1L << 3), - MWM_DECOR_MENU = (1L << 4), - MWM_DECOR_MINIMIZE = (1L << 5), - MWM_DECOR_MAXIMIZE = (1L << 6), - - MWM_HINTS_INPUT_MODE = (1L << 2), - - MWM_INPUT_MODELESS = 0L, - MWM_INPUT_PRIMARY_APPLICATION_MODAL = 1L, - MWM_INPUT_FULL_APPLICATION_MODAL = 3L -}; - -static Atom mwm_hint_atom = XNone; - -#if 0 -static QtMWMHints GetMWMHints(Display *display, Window window) +QtMWMHints QTestLiteWindow::getMWMHints() const { QtMWMHints mwmhints; @@ -790,10 +364,11 @@ static QtMWMHints GetMWMHints(Display *display, Window window) int format; ulong nitems, bytesLeft; uchar *data = 0; - if ((XGetWindowProperty(display, window, mwm_hint_atom, 0, 5, false, - mwm_hint_atom, &type, &format, &nitems, &bytesLeft, + Atom atomForMotifWmHints = QTestLiteStatic::atom(QTestLiteStatic::_MOTIF_WM_HINTS); + if ((XGetWindowProperty(mScreen->display(), x_window, atomForMotifWmHints, 0, 5, false, + atomForMotifWmHints, &type, &format, &nitems, &bytesLeft, &data) == Success) - && (type == mwm_hint_atom + && (type == atomForMotifWmHints && format == 32 && nitems >= 5)) { mwmhints = *(reinterpret_cast<QtMWMHints *>(data)); @@ -810,15 +385,16 @@ static QtMWMHints GetMWMHints(Display *display, Window window) return mwmhints; } -#endif -static void SetMWMHints(Display *display, Window window, const QtMWMHints &mwmhints) +void QTestLiteWindow::setMWMHints(const QtMWMHints &mwmhints) { + Atom atomForMotifWmHints = QTestLiteStatic::atom(QTestLiteStatic::_MOTIF_WM_HINTS); if (mwmhints.flags != 0l) { - XChangeProperty(display, window, mwm_hint_atom, mwm_hint_atom, 32, + XChangeProperty(mScreen->display(), x_window, + atomForMotifWmHints, atomForMotifWmHints, 32, PropModeReplace, (unsigned char *) &mwmhints, 5); } else { - XDeleteProperty(display, window, mwm_hint_atom); + XDeleteProperty(mScreen->display(), x_window, atomForMotifWmHints); } } @@ -835,17 +411,11 @@ static inline bool isTransient(const QWidget *w) && !w->testAttribute(Qt::WA_X11BypassTransientForHint)); } - - Qt::WindowFlags QTestLiteWindow::setWindowFlags(Qt::WindowFlags flags) { // Q_ASSERT(flags & Qt::Window); window_flags = flags; - if (mwm_hint_atom == XNone) { - mwm_hint_atom = XInternAtom(xd->display, "_MOTIF_WM_HINTS\0", False); - } - #ifdef MYX11_DEBUG qDebug() << "QTestLiteWindow::setWindowFlags" << hex << x_window << "flags" << flags; #endif @@ -864,12 +434,10 @@ Qt::WindowFlags QTestLiteWindow::setWindowFlags(Qt::WindowFlags flags) bool tool = (type == Qt::Tool || type == Qt::SplashScreen || type == Qt::ToolTip || type == Qt::Drawer); - Q_UNUSED(topLevel); Q_UNUSED(dialog); Q_UNUSED(desktop); - bool tooltip = (type == Qt::ToolTip); XSetWindowAttributes wsa; @@ -950,7 +518,7 @@ Qt::WindowFlags QTestLiteWindow::setWindowFlags(Qt::WindowFlags flags) mwmhints.decorations = 0; } - SetMWMHints(xd->display, x_window, mwmhints); + setMWMHints(mwmhints); //##### only if initializeWindow??? @@ -963,7 +531,7 @@ Qt::WindowFlags QTestLiteWindow::setWindowFlags(Qt::WindowFlags flags) wsa.override_redirect = True; wsa.save_under = True; - XChangeWindowAttributes(xd->display, x_window, CWOverrideRedirect | CWSaveUnder, + XChangeWindowAttributes(mScreen->display(), x_window, CWOverrideRedirect | CWSaveUnder, &wsa); } else { #ifdef MYX11_DEBUG @@ -979,39 +547,19 @@ void QTestLiteWindow::setVisible(bool visible) #ifdef MYX11_DEBUG qDebug() << "QTestLiteWindow::setVisible" << visible << hex << x_window; #endif - if (visible) - XMapWindow(xd->display, x_window); - else - XUnmapWindow(xd->display, x_window); -} - - -void QTestLiteWindow::setCursor(QCursor * cursor) -{ - int id = cursor->handle(); - if (id == currentCursor) - return; - Cursor c; - if (!xd->cursors->exists(id)) { - if (cursor->shape() == Qt::BitmapCursor) - c = createCursorBitmap(cursor); - else - c = createCursorShape(cursor->shape()); - if (!c) { - return; - } - xd->cursors->createNode(id, c); + if (visible) { + //ensure that the window is viewed in correct position. + doSizeHints(); + XMapWindow(mScreen->display(), x_window); } else { - xd->cursors->incrementUseCount(id); - c = xd->cursors->cursor(id); + XUnmapWindow(mScreen->display(), x_window); } +} - if (currentCursor != -1) - xd->cursors->decrementUseCount(currentCursor); - currentCursor = id; - - XDefineCursor(xd->display, x_window, c); - XFlush(xd->display); +void QTestLiteWindow::setCursor(const Cursor &cursor) +{ + XDefineCursor(mScreen->display(), x_window, cursor); + XFlush(mScreen->display()); } QPlatformGLContext *QTestLiteWindow::glContext() const @@ -1020,567 +568,78 @@ QPlatformGLContext *QTestLiteWindow::glContext() const return 0; if (!mGLContext) { QTestLiteWindow *that = const_cast<QTestLiteWindow *>(this); -#ifndef QT_NO_OPENGL - that->mGLContext = new QGLXGLContext(x_window, xd,widget()->platformWindowFormat()); +#if !defined(QT_NO_OPENGL) +#if !defined(QT_OPENGL_ES_2) + that->mGLContext = new QGLXContext(x_window, mScreen,widget()->platformWindowFormat()); +#else + EGLDisplay display = eglGetDisplay(mScreen->display()); + + QPlatformWindowFormat windowFormat = correctColorBuffers(widget()->platformWindowFormat()); + + EGLConfig config = q_configFromQPlatformWindowFormat(display,windowFormat); + QVector<EGLint> eglContextAttrs; + eglContextAttrs.append(EGL_CONTEXT_CLIENT_VERSION); + eglContextAttrs.append(2); + eglContextAttrs.append(EGL_NONE); + + EGLSurface eglSurface = eglCreateWindowSurface(display,config,(EGLNativeWindowType)x_window,0); + that->mGLContext = new QEGLPlatformContext(display, config, eglContextAttrs.data(), eglSurface, EGL_OPENGL_ES_API); +#endif #endif } return mGLContext; } -Cursor QTestLiteWindow::createCursorBitmap(QCursor * cursor) -{ - XColor bg, fg; - bg.red = 255 << 8; - bg.green = 255 << 8; - bg.blue = 255 << 8; - fg.red = 0; - fg.green = 0; - fg.blue = 0; - QPoint spot = cursor->hotSpot(); - Window rootwin = x_window; - - QImage mapImage = cursor->bitmap()->toImage().convertToFormat(QImage::Format_MonoLSB); - QImage maskImage = cursor->mask()->toImage().convertToFormat(QImage::Format_MonoLSB); - - int width = cursor->bitmap()->width(); - int height = cursor->bitmap()->height(); - int bytesPerLine = mapImage.bytesPerLine(); - int destLineSize = width / 8; - if (width % 8) - destLineSize++; - - const uchar * map = mapImage.bits(); - const uchar * mask = maskImage.bits(); - - char * mapBits = new char[height * destLineSize]; - char * maskBits = new char[height * destLineSize]; - for (int i = 0; i < height; i++) { - memcpy(mapBits + (destLineSize * i),map + (bytesPerLine * i), destLineSize); - memcpy(maskBits + (destLineSize * i),mask + (bytesPerLine * i), destLineSize); - } - - Pixmap cp = XCreateBitmapFromData(xd->display, rootwin, mapBits, width, height); - Pixmap mp = XCreateBitmapFromData(xd->display, rootwin, maskBits, width, height); - Cursor c = XCreatePixmapCursor(xd->display, cp, mp, &fg, &bg, spot.x(), spot.y()); - XFreePixmap(xd->display, cp); - XFreePixmap(xd->display, mp); - delete[] mapBits; - delete[] maskBits; - - return c; -} - -Cursor QTestLiteWindow::createCursorShape(int cshape) -{ - Cursor cursor = 0; - - if (cshape < 0 || cshape > Qt::LastCursor) - return 0; - - switch (cshape) { - case Qt::ArrowCursor: - cursor = XCreateFontCursor(xd->display, XC_left_ptr); - break; - case Qt::UpArrowCursor: - cursor = XCreateFontCursor(xd->display, XC_center_ptr); - break; - case Qt::CrossCursor: - cursor = XCreateFontCursor(xd->display, XC_crosshair); - break; - case Qt::WaitCursor: - cursor = XCreateFontCursor(xd->display, XC_watch); - break; - case Qt::IBeamCursor: - cursor = XCreateFontCursor(xd->display, XC_xterm); - break; - case Qt::SizeAllCursor: - cursor = XCreateFontCursor(xd->display, XC_fleur); - break; - case Qt::PointingHandCursor: - cursor = XCreateFontCursor(xd->display, XC_hand2); - break; - case Qt::SizeBDiagCursor: - cursor = XCreateFontCursor(xd->display, XC_top_right_corner); - break; - case Qt::SizeFDiagCursor: - cursor = XCreateFontCursor(xd->display, XC_bottom_right_corner); - break; - case Qt::SizeVerCursor: - case Qt::SplitVCursor: - cursor = XCreateFontCursor(xd->display, XC_sb_v_double_arrow); - break; - case Qt::SizeHorCursor: - case Qt::SplitHCursor: - cursor = XCreateFontCursor(xd->display, XC_sb_h_double_arrow); - break; - case Qt::WhatsThisCursor: - cursor = XCreateFontCursor(xd->display, XC_question_arrow); - break; - case Qt::ForbiddenCursor: - cursor = XCreateFontCursor(xd->display, XC_circle); - break; - case Qt::BusyCursor: - cursor = XCreateFontCursor(xd->display, XC_watch); - break; - - default: //default cursor for all the rest - break; - } - return cursor; -} - - -MyX11Cursors::MyX11Cursors(Display * d) : firstExpired(0), lastExpired(0), display(d), removalDelay(3) -{ - connect(&timer, SIGNAL(timeout()), this, SLOT(timeout())); -} - -void MyX11Cursors::insertNode(MyX11CursorNode * node) +Window QTestLiteWindow::xWindow() const { - QDateTime now = QDateTime::currentDateTime(); - QDateTime timeout = now.addSecs(removalDelay); - node->setExpiration(timeout); - node->setPost(0); - if (lastExpired) { - lastExpired->setPost(node); - node->setAnte(lastExpired); - } - lastExpired = node; - if (!firstExpired) { - firstExpired = node; - node->setAnte(0); - int interval = removalDelay * 1000; - timer.setInterval(interval); - timer.start(); - } -} - -void MyX11Cursors::removeNode(MyX11CursorNode * node) -{ - MyX11CursorNode *pre = node->ante(); - MyX11CursorNode *post = node->post(); - if (pre) - pre->setPost(post); - if (post) - post->setAnte(pre); - if (node == lastExpired) - lastExpired = pre; - if (node == firstExpired) { - firstExpired = post; - if (!firstExpired) { - timer.stop(); - return; - } - int interval = QDateTime::currentDateTime().secsTo(firstExpired->expiration()) * 1000; - timer.stop(); - timer.setInterval(interval); - timer.start(); - } -} - -void MyX11Cursors::incrementUseCount(int id) -{ - MyX11CursorNode * node = lookupMap.value(id); - Q_ASSERT(node); - if (!node->refCount) - removeNode(node); - node->refCount++; -} - -void MyX11Cursors::decrementUseCount(int id) -{ - MyX11CursorNode * node = lookupMap.value(id); - Q_ASSERT(node); - node->refCount--; - if (!node->refCount) - insertNode(node); -} - -void MyX11Cursors::createNode(int id, Cursor c) -{ - MyX11CursorNode * node = new MyX11CursorNode(id, c); - lookupMap.insert(id, node); -} - -void MyX11Cursors::timeout() -{ - MyX11CursorNode * node; - node = firstExpired; - QDateTime now = QDateTime::currentDateTime(); - while (node && now.secsTo(node->expiration()) < 1) { - Cursor c = node->cursor(); - int id = node->id(); - lookupMap.take(id); - MyX11CursorNode * tmp = node; - node = node->post(); - if (node) - node->setAnte(0); - delete tmp; - XFreeCursor(display, c); - } - firstExpired = node; - if (node == 0) { - timer.stop(); - lastExpired = 0; - } - else { - int interval = QDateTime::currentDateTime().secsTo(firstExpired->expiration()) * 1000; - timer.setInterval(interval); - timer.start(); - } + return x_window; } -Cursor MyX11Cursors::cursor(int id) +GC QTestLiteWindow::graphicsContext() const { - MyX11CursorNode * node = lookupMap.value(id); - Q_ASSERT(node); - return node->cursor(); + return gc; } - - -/******************************************************************** - -MyDisplay class - perhaps better placed in qplatformintegration_testlite? - -*********************************************************************/ - -//### copied from qapplication_x11.cpp - -static int qt_x_errhandler(Display *dpy, XErrorEvent *err) -{ - -qDebug() << "qt_x_errhandler" << err->error_code; - - switch (err->error_code) { - case BadAtom: -#if 0 - if (err->request_code == 20 /* X_GetProperty */ - && (err->resourceid == XA_RESOURCE_MANAGER - || err->resourceid == XA_RGB_DEFAULT_MAP - || err->resourceid == ATOM(_NET_SUPPORTED) - || err->resourceid == ATOM(_NET_SUPPORTING_WM_CHECK) - || err->resourceid == ATOM(KDE_FULL_SESSION) - || err->resourceid == ATOM(KWIN_RUNNING) - || err->resourceid == ATOM(XdndProxy) - || err->resourceid == ATOM(XdndAware)) - - - ) { - // Perhaps we're running under SECURITY reduction? :/ - return 0; - } -#endif - qDebug() << "BadAtom"; - break; - - case BadWindow: - if (err->request_code == 2 /* X_ChangeWindowAttributes */ - || err->request_code == 38 /* X_QueryPointer */) { - for (int i = 0; i < ScreenCount(dpy); ++i) { - if (err->resourceid == RootWindow(dpy, i)) { - // Perhaps we're running under SECURITY reduction? :/ - return 0; - } - } - } - seen_badwindow = true; - if (err->request_code == 25 /* X_SendEvent */) { - for (int i = 0; i < ScreenCount(dpy); ++i) { - if (err->resourceid == RootWindow(dpy, i)) { - // Perhaps we're running under SECURITY reduction? :/ - return 0; - } - } -#if 0 - if (X11->xdndHandleBadwindow()) { - qDebug("xdndHandleBadwindow returned true"); - return 0; - } -#endif - } -#if 0 - if (X11->ignore_badwindow) - return 0; -#endif - break; - - case BadMatch: - if (err->request_code == 42 /* X_SetInputFocus */) - return 0; - break; - - default: -#if 0 //!defined(QT_NO_XINPUT) - if (err->request_code == X11->xinput_major - && err->error_code == (X11->xinput_errorbase + XI_BadDevice) - && err->minor_code == 3 /* X_OpenDevice */) { - return 0; - } -#endif - break; - } - - char errstr[256]; - XGetErrorText( dpy, err->error_code, errstr, 256 ); - char buffer[256]; - char request_str[256]; - qsnprintf(buffer, 256, "%d", err->request_code); - XGetErrorDatabaseText(dpy, "XRequest", buffer, "", request_str, 256); - if (err->request_code < 128) { - // X error for a normal protocol request - qWarning( "X Error: %s %d\n" - " Major opcode: %d (%s)\n" - " Resource id: 0x%lx", - errstr, err->error_code, - err->request_code, - request_str, - err->resourceid ); +void QTestLiteWindow::doSizeHints() +{ + Q_ASSERT(widget()->testAttribute(Qt::WA_WState_Created)); + XSizeHints s; + s.flags = 0; + QRect g = geometry(); + s.x = g.x(); + s.y = g.y(); + s.width = g.width(); + s.height = g.height(); + s.flags |= USPosition; + s.flags |= PPosition; + s.flags |= USSize; + s.flags |= PSize; + s.flags |= PWinGravity; + s.win_gravity = QApplication::isRightToLeft() ? NorthEastGravity : NorthWestGravity; + XSetWMNormalHints(mScreen->display(), x_window, &s); +} + +QPlatformWindowFormat QTestLiteWindow::correctColorBuffers(const QPlatformWindowFormat &platformWindowFormat) const +{ + // I have only tested this setup on a dodgy intel setup, where I didn't use standard libs, + // so this might be not what we want to do :) + if ( !(platformWindowFormat.redBufferSize() == -1 && + platformWindowFormat.greenBufferSize() == -1 && + platformWindowFormat.blueBufferSize() == -1)) + return platformWindowFormat; + + QPlatformWindowFormat windowFormat = platformWindowFormat; + if (mScreen->depth() == 16) { + windowFormat.setRedBufferSize(5); + windowFormat.setGreenBufferSize(6); + windowFormat.setBlueBufferSize(5); } else { - // X error for an extension request - const char *extensionName = 0; -#if 0 - if (err->request_code == X11->xrender_major) - extensionName = "RENDER"; - else if (err->request_code == X11->xrandr_major) - extensionName = "RANDR"; - else if (err->request_code == X11->xinput_major) - extensionName = "XInputExtension"; - else if (err->request_code == X11->mitshm_major) - extensionName = "MIT-SHM"; -#endif - char minor_str[256]; - if (extensionName) { - qsnprintf(buffer, 256, "%s.%d", extensionName, err->minor_code); - XGetErrorDatabaseText(dpy, "XRequest", buffer, "", minor_str, 256); - } else { - extensionName = "Uknown extension"; - qsnprintf(minor_str, 256, "Unknown request"); - } - qWarning( "X Error: %s %d\n" - " Extension: %d (%s)\n" - " Minor opcode: %d (%s)\n" - " Resource id: 0x%lx", - errstr, err->error_code, - err->request_code, - extensionName, - err->minor_code, - minor_str, - err->resourceid ); + windowFormat.setRedBufferSize(8); + windowFormat.setGreenBufferSize(8); + windowFormat.setBlueBufferSize(8); } - // ### we really should distinguish between severe, non-severe and - // ### application specific errors - - return 0; + return windowFormat; } - -#ifdef KeyPress -#undef KeyPress -#endif -#ifdef KeyRelease -#undef KeyRelease -#endif - -bool MyDisplay::handleEvent(XEvent *xe) -{ - //qDebug() << "handleEvent" << xe->xany.type << xe->xany.window; - int quit = false; - QTestLiteWindow *xw = 0; - foreach (QTestLiteWindow *w, windowList) { - if (w->winId() == xe->xany.window) { - xw = w; - break; - } - } - if (!xw) { -#ifdef MYX11_DEBUG - qWarning() << "Unknown window" << hex << xe->xany.window << "received event" << xe->type; -#endif - return quit; - } - - switch (xe->type) { - - case ClientMessage: - if (xe->xclient.format == 32 && xe->xclient.message_type == wmProtocolsAtom) { - Atom a = xe->xclient.data.l[0]; - if (a == wmDeleteWindowAtom) - xw->handleCloseEvent(); -#ifdef MYX11_DEBUG - qDebug() << "ClientMessage WM_PROTOCOLS" << a; -#endif - } -#ifdef MYX11_DEBUG - else - qDebug() << "ClientMessage" << xe->xclient.format << xe->xclient.message_type; -#endif - break; - - case Expose: - if (xw) - if (xe->xexpose.count == 0) - xw->paintEvent(); - break; - case ConfigureNotify: - if (xw) - xw->resizeEvent(&xe->xconfigure); - break; - - case ButtonPress: - xw->mousePressEvent(&xe->xbutton); - break; - - case ButtonRelease: - xw->handleMouseEvent(QEvent::MouseButtonRelease, &xe->xbutton); - break; - - case MotionNotify: - xw->handleMouseEvent(QEvent::MouseMove, &xe->xbutton); - break; - - case XKeyPress: - xw->handleKeyEvent(QEvent::KeyPress, &xe->xkey); - break; - - case XKeyRelease: - xw->handleKeyEvent(QEvent::KeyRelease, &xe->xkey); - break; - - case EnterNotify: - xw->handleEnterEvent(); - break; - - case LeaveNotify: - xw->handleLeaveEvent(); - break; - - default: -#ifdef MYX11_DEBUG - qDebug() << hex << xe->xany.window << "Other X event" << xe->type; -#endif - break; - } - return quit; -}; - - - -MyDisplay::MyDisplay() -{ - char *display_name = getenv("DISPLAY"); - display = XOpenDisplay(display_name); - if (!display) { - fprintf(stderr, "Cannot connect to X server: %s\n", - display_name); - exit(1); - } - -#ifndef DONT_USE_MIT_SHM - Status MIT_SHM_extension_supported = XShmQueryExtension (display); - Q_ASSERT(MIT_SHM_extension_supported == True); -#endif - original_x_errhandler = XSetErrorHandler(qt_x_errhandler); - - if (qgetenv("DO_X_SYNCHRONIZE").toInt()) - XSynchronize(display, true); - - screen = DefaultScreen(display); - width = DisplayWidth(display, screen); - height = DisplayHeight(display, screen); - physicalWidth = DisplayWidthMM(display, screen); - physicalHeight = DisplayHeightMM(display, screen); - - int xSocketNumber = XConnectionNumber(display); -#ifdef MYX11_DEBUG - qDebug() << "X socket:"<< xSocketNumber; -#endif - QSocketNotifier *sock = new QSocketNotifier(xSocketNumber, QSocketNotifier::Read, this); - connect(sock, SIGNAL(activated(int)), this, SLOT(eventDispatcher())); - - wmProtocolsAtom = XInternAtom (display, "WM_PROTOCOLS", False); - wmDeleteWindowAtom = XInternAtom (display, "WM_DELETE_WINDOW", False); - - cursors = new MyX11Cursors(display); -} - - -MyDisplay::~MyDisplay() -{ - XCloseDisplay(display); -} - - -void MyDisplay::eventDispatcher() -{ -// qDebug() << "eventDispatcher"; - - - ulong marker = XNextRequest(display); -// int i = 0; - while (XPending(display)) { - XEvent event; - XNextEvent(display, &event); - /* done = */ - handleEvent(&event); - - if (event.xany.serial >= marker) { -#ifdef MYX11_DEBUG - qDebug() << "potential livelock averted"; -#endif -#if 0 - if (XEventsQueued(display, QueuedAfterFlush)) { - qDebug() << " with events queued"; - QTimer::singleShot(0, this, SLOT(eventDispatcher())); - } -#endif - break; - } - } -} - - -QImage MyDisplay::grabWindow(Window window, int x, int y, int w, int h) -{ - if (w == 0 || h ==0) - return QImage(); - - //WinId 0 means the desktop widget - if (!window) - window = rootWindow(); - - XWindowAttributes window_attr; - if (!XGetWindowAttributes(display, window, &window_attr)) - return QImage(); - - if (w < 0) - w = window_attr.width - x; - if (h < 0) - h = window_attr.height - y; - - // Ideally, we should also limit ourselves to the screen area, but the Qt docs say - // that it's "unsafe" to go outside the screen, so we can ignore that problem. - - //We're definitely not optimizing for speed... - XImage *xi = XGetImage(display, window, x, y, w, h, AllPlanes, ZPixmap); - - if (!xi) - return QImage(); - - //taking a copy to make sure we have ownership -- not fast - QImage result = QImage( (uchar*) xi->data, xi->width, xi->height, xi->bytes_per_line, QImage::Format_RGB32 ).copy(); - - XDestroyImage(xi); - - return result; -} - - - - - - - QT_END_NAMESPACE -#include "qtestlitewindow.moc" diff --git a/src/plugins/platforms/testlite/qtestlitewindow.h b/src/plugins/platforms/testlite/qtestlitewindow.h index dc628f1..e45c3fd 100644 --- a/src/plugins/platforms/testlite/qtestlitewindow.h +++ b/src/plugins/platforms/testlite/qtestlitewindow.h @@ -42,77 +42,67 @@ #ifndef QTESTLITEWINDOW_H #define QTESTLITEWINDOW_H +#include "qtestliteintegration.h" + #include <QPlatformWindow> -#include <qevent.h> +#include <QEvent> #include <QObject> #include <QImage> -#include <qtimer.h> -#include <QDateTime> - -#include <private/qt_x11_p.h> - -#include <X11/Xlib.h> -#include <X11/Xutil.h> - - - -class QTestLiteIntegration; -class QTestLiteScreen; -class QTestLiteWindowSurface; -class MyX11Cursors; -class QTestLiteWindow; -class MyDisplay : public QObject -{ - Q_OBJECT; -public: - MyDisplay(); - ~MyDisplay(); +struct QtMWMHints { + ulong flags, functions, decorations; + long input_mode; + ulong status; +}; - Window rootWindow() { return RootWindow(display, screen); } - unsigned long blackPixel() { return BlackPixel(display, screen); } - unsigned long whitePixel() { return WhitePixel(display, screen); } +enum { + MWM_HINTS_FUNCTIONS = (1L << 0), - bool handleEvent(XEvent *xe); - QImage grabWindow(Window window, int x, int y, int w, int h); + MWM_FUNC_ALL = (1L << 0), + MWM_FUNC_RESIZE = (1L << 1), + MWM_FUNC_MOVE = (1L << 2), + MWM_FUNC_MINIMIZE = (1L << 3), + MWM_FUNC_MAXIMIZE = (1L << 4), + MWM_FUNC_CLOSE = (1L << 5), -public slots: - void eventDispatcher(); + MWM_HINTS_DECORATIONS = (1L << 1), -public: //### - Display * display; - int screen; - int width, height; - int physicalWidth; - int physicalHeight; + MWM_DECOR_ALL = (1L << 0), + MWM_DECOR_BORDER = (1L << 1), + MWM_DECOR_RESIZEH = (1L << 2), + MWM_DECOR_TITLE = (1L << 3), + MWM_DECOR_MENU = (1L << 4), + MWM_DECOR_MINIMIZE = (1L << 5), + MWM_DECOR_MAXIMIZE = (1L << 6), - QList<QTestLiteWindow*> windowList; + MWM_HINTS_INPUT_MODE = (1L << 2), - MyX11Cursors * cursors; + MWM_INPUT_MODELESS = 0L, + MWM_INPUT_PRIMARY_APPLICATION_MODAL = 1L, + MWM_INPUT_FULL_APPLICATION_MODAL = 3L }; -struct MyShmImageInfo; - class QTestLiteWindow : public QPlatformWindow { public: - QTestLiteWindow(const QTestLiteIntegration *platformIntegration, - QTestLiteScreen *screen, QWidget *window); + QTestLiteWindow(QWidget *window); ~QTestLiteWindow(); void mousePressEvent(XButtonEvent*); void handleMouseEvent(QEvent::Type, XButtonEvent *ev); - void handleKeyEvent(QEvent::Type, void *); void handleCloseEvent(); void handleEnterEvent(); void handleLeaveEvent(); + void handleFocusInEvent(); + void handleFocusOutEvent(); void resizeEvent(XConfigureEvent *configure_event); void paintEvent(); + void requestActivateWindow(); void setGeometry(const QRect &rect); @@ -125,33 +115,30 @@ public: void lower(); void setWindowTitle(const QString &title); - void setCursor(QCursor * cursor); + void setCursor(const Cursor &cursor); QPlatformGLContext *glContext() const; + Window xWindow() const; + GC graphicsContext() const; + +protected: + void setMWMHints(const QtMWMHints &mwmhints); + QtMWMHints getMWMHints() const; + + void doSizeHints(); + private: - int xpos, ypos; - int width, height; + QPlatformWindowFormat correctColorBuffers(const QPlatformWindowFormat &windowFormat)const; + Window x_window; GC gc; GC createGC(); - Cursor createCursorShape(int cshape); - Cursor createCursorBitmap(QCursor * cursor); - - int currentCursor; - - MyDisplay *xd; + QPlatformGLContext *mGLContext; QTestLiteScreen *mScreen; Qt::WindowFlags window_flags; - QPlatformGLContext *mGLContext; - - friend class QTestLiteWindowSurface; // x_window, gc and windowSurface }; - - - - #endif diff --git a/src/plugins/platforms/testlite/qtestlitewindowsurface.cpp b/src/plugins/platforms/testlite/qtestlitewindowsurface.cpp index b3232c8..ced964a 100644 --- a/src/plugins/platforms/testlite/qtestlitewindowsurface.cpp +++ b/src/plugins/platforms/testlite/qtestlitewindowsurface.cpp @@ -46,6 +46,7 @@ #include <QWindowSystemInterface> #include "qtestlitewindow.h" +#include "qtestlitescreen.h" # include <sys/ipc.h> # include <sys/shm.h> @@ -65,8 +66,6 @@ struct MyShmImageInfo { Display *display; }; -//void QTestLiteWindowSurface::flush() - #ifndef DONT_USE_MIT_SHM void MyShmImageInfo::destroy() @@ -80,20 +79,21 @@ void MyShmImageInfo::destroy() void QTestLiteWindowSurface::resizeShmImage(int width, int height) { - MyDisplay *xd = xw->xd; #ifdef DONT_USE_MIT_SHM shm_img = QImage(width, height, QImage::Format_RGB32); #else + + QTestLiteScreen *screen = QTestLiteScreen::testLiteScreenForWidget(window()); if (image_info) image_info->destroy(); else - image_info = new MyShmImageInfo(xd->display); + image_info = new MyShmImageInfo(screen->display()); - Visual *visual = DefaultVisual(xd->display, xd->screen); + Visual *visual = DefaultVisual(screen->display(), screen->xScreenNumber()); - XImage *image = XShmCreateImage (xd->display, visual, 24, ZPixmap, 0, + XImage *image = XShmCreateImage (screen->display(), visual, 24, ZPixmap, 0, &image_info->shminfo, width, height); @@ -105,7 +105,7 @@ void QTestLiteWindowSurface::resizeShmImage(int width, int height) image_info->image = image; - Status shm_attach_status = XShmAttach(xd->display, &image_info->shminfo); + Status shm_attach_status = XShmAttach(screen->display(), &image_info->shminfo); Q_ASSERT(shm_attach_status == True); @@ -126,7 +126,7 @@ QSize QTestLiteWindowSurface::bufferSize() const return shm_img.size(); } -QTestLiteWindowSurface::QTestLiteWindowSurface (QTestLiteScreen */*screen*/, QWidget *window) +QTestLiteWindowSurface::QTestLiteWindowSurface (QWidget *window) : QWindowSurface(window), painted(false), image_info(0) { @@ -151,29 +151,27 @@ void QTestLiteWindowSurface::flush(QWidget *widget, const QRegion ®ion, const Q_UNUSED(region); Q_UNUSED(offset); - // qDebug() << "QTestLiteWindowSurface::flush:" << (long)this; - if (!painted) return; - MyDisplay *xd = xw->xd; - GC gc = xw->gc; - Window window = xw->x_window; + QTestLiteScreen *screen = QTestLiteScreen::testLiteScreenForWidget(widget); + GC gc = xw->graphicsContext(); + Window window = xw->xWindow(); #ifdef DONT_USE_MIT_SHM // just convert the image every time... if (!shm_img.isNull()) { - Visual *visual = DefaultVisual(xd->display, xd->screen); + Visual *visual = DefaultVisual(screen->display(), screen->xScreenNumber()); QImage image = shm_img; //img.convertToFormat( - XImage *xi = XCreateImage(xd->display, visual, 24, ZPixmap, + XImage *xi = XCreateImage(screen->display(), visual, 24, ZPixmap, 0, (char *) image.scanLine(0), image.width(), image.height(), 32, image.bytesPerLine()); int x = 0; int y = 0; - /*int r =*/ XPutImage(xd->display, window, gc, xi, 0, 0, x, y, image.width(), image.height()); + /*int r =*/ XPutImage(screen->display(), window, gc, xi, 0, 0, x, y, image.width(), image.height()); xi->data = 0; // QImage owns these bits XDestroyImage(xi); @@ -187,11 +185,11 @@ void QTestLiteWindowSurface::flush(QWidget *widget, const QRegion ®ion, const // We could set send_event to true, and then use the ShmCompletion to synchronize, // but let's do like Qt/11 and just use XSync - XShmPutImage (xd->display, window, gc, image_info->image, 0, 0, + XShmPutImage (screen->display(), window, gc, image_info->image, 0, 0, x, y, image_info->image->width, image_info->image->height, /*send_event*/ False); - XSync(xd->display, False); + XSync(screen->display(), False); } #endif } diff --git a/src/plugins/platforms/testlite/qtestlitewindowsurface.h b/src/plugins/platforms/testlite/qtestlitewindowsurface.h index 915e7fe..ca900e5 100644 --- a/src/plugins/platforms/testlite/qtestlitewindowsurface.h +++ b/src/plugins/platforms/testlite/qtestlitewindowsurface.h @@ -55,7 +55,7 @@ class MyShmImageInfo; class QTestLiteWindowSurface : public QWindowSurface { public: - QTestLiteWindowSurface (QTestLiteScreen *screen, QWidget *window); + QTestLiteWindowSurface (QWidget *window); ~QTestLiteWindowSurface(); QPaintDevice *paintDevice(); diff --git a/src/plugins/platforms/testlite/testlite.pro b/src/plugins/platforms/testlite/testlite.pro index 05bd384..7fb3304 100644 --- a/src/plugins/platforms/testlite/testlite.pro +++ b/src/plugins/platforms/testlite/testlite.pro @@ -7,22 +7,50 @@ SOURCES = \ main.cpp \ qtestliteintegration.cpp \ qtestlitewindowsurface.cpp \ - qtestlitewindow.cpp + qtestlitewindow.cpp \ + qtestlitecursor.cpp \ + qtestlitescreen.cpp \ + qtestlitekeyboard.cpp \ + qtestliteclipboard.cpp \ + qtestlitemime.cpp \ + qtestlitestaticinfo.cpp HEADERS = \ qtestliteintegration.h \ qtestlitewindowsurface.h \ - qtestlitewindow.h - + qtestlitewindow.h \ + qtestlitecursor.h \ + qtestlitescreen.h \ + qtestlitekeyboard.h \ + qtestliteclipboard.h \ + qtestlitemime.h \ + qtestlitestaticinfo.h LIBS += -lX11 -lXext +mac { + LIBS += -L/usr/X11/lib -lz -framework Carbon +} + include (../fontdatabases/genericunix/genericunix.pri) contains(QT_CONFIG, opengl) { QT += opengl - HEADERS += qglxintegration.h - SOURCES += qglxintegration.cpp + !contains(QT_CONFIG, opengles2) { + HEADERS += qglxintegration.h + SOURCES += qglxintegration.cpp + } else { # There is no easy way to detect if we'r suppose to use glx or not + HEADERS += \ + ../eglconvenience/qeglplatformcontext.h \ + ../eglconvenience/qeglconvenience.h \ + qtestliteeglintegration.h + + SOURCES += \ + ../eglconvenience/qeglplatformcontext.cpp \ + ../eglconvenience/qeglconvenience.cpp \ + qtestliteeglintegration.cpp + LIBS += -lEGL + } } target.path += $$[QT_INSTALL_PLUGINS]/platforms diff --git a/src/plugins/platforms/wayland/main.cpp b/src/plugins/platforms/wayland/main.cpp new file mode 100644 index 0000000..1bca661 --- /dev/null +++ b/src/plugins/platforms/wayland/main.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/QPlatformIntegrationPlugin> +#include "qwaylandintegration.h" + +QT_BEGIN_NAMESPACE + +class QWaylandIntegrationPlugin : public QPlatformIntegrationPlugin +{ +public: + QStringList keys() const; + QPlatformIntegration *create(const QString&, const QStringList&); +}; + +QStringList QWaylandIntegrationPlugin::keys() const +{ + QStringList list; + list << "Wayland"; + list << "WaylandGL"; + return list; +} + +QPlatformIntegration *QWaylandIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + if (system.toLower() == "wayland") + return new QWaylandIntegration; + if (system.toLower() == "waylandgl") + return new QWaylandIntegration(true); + + return 0; +} + +Q_EXPORT_PLUGIN2(wayland, QWaylandIntegrationPlugin) + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/qwaylandbuffer.h b/src/plugins/platforms/wayland/qwaylandbuffer.h new file mode 100644 index 0000000..643e89c --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandbuffer.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDBUFFER_H +#define QWAYLANDBUFFER_H + +#include <QtCore/QSize> + +#include <wayland-client-protocol.h> + +class QWaylandBuffer { +public: + QWaylandBuffer() { } + virtual ~QWaylandBuffer() { } + wl_buffer *buffer() {return mBuffer;} + virtual QSize size() const = 0; + +protected: + struct wl_buffer *mBuffer; +}; + +#endif // QWAYLANDBUFFER_H diff --git a/src/plugins/platforms/wayland/qwaylandcursor.cpp b/src/plugins/platforms/wayland/qwaylandcursor.cpp new file mode 100644 index 0000000..29c6abd --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandcursor.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandcursor.h" + +#include "qwaylanddisplay.h" +#include "qwaylandinputdevice.h" +#include "qwaylandshmsurface.h" +#include "qwaylandscreen.h" + +#include <QtGui/QImageReader> + +#define DATADIR "/home/jlind/install/share" + +static const struct pointer_image { + const char *filename; + int hotspot_x, hotspot_y; +} pointer_images[] = { + /* FIXME: Half of these are wrong... */ + /* Qt::ArrowCursor */ + { DATADIR "/wayland/left_ptr.png", 10, 5 }, + /* Qt::UpArrowCursor */ + { DATADIR "/wayland/top_side.png", 18, 8 }, + /* Qt::CrossCursor */ + { DATADIR "/wayland/top_side.png", 18, 8 }, + /* Qt::WaitCursor */ + { DATADIR "/wayland/top_side.png", 18, 8 }, + /* Qt::IBeamCursor */ + { DATADIR "/wayland/xterm.png", 15, 15 }, + /* Qt::SizeVerCursor */ + { DATADIR "/wayland/top_side.png", 18, 8 }, + /* Qt::SizeHorCursor */ + { DATADIR "/wayland/bottom_left_corner.png", 6, 30 }, + /* Qt::SizeBDiagCursor */ + { DATADIR "/wayland/bottom_right_corner.png", 28, 28 }, + /* Qt::SizeFDiagCursor */ + { DATADIR "/wayland/bottom_side.png", 16, 20 }, + /* Qt::SizeAllCursor */ + { DATADIR "/wayland/left_side.png", 10, 20 }, + /* Qt::BlankCursor */ + { DATADIR "/wayland/right_side.png", 30, 19 }, + /* Qt::SplitVCursor */ + { DATADIR "/wayland/sb_v_double_arrow.png", 15, 15 }, + /* Qt::SplitHCursor */ + { DATADIR "/wayland/sb_h_double_arrow.png", 15, 15 }, + /* Qt::PointingHandCursor */ + { DATADIR "/wayland/hand2.png", 14, 8 }, + /* Qt::ForbiddenCursor */ + { DATADIR "/wayland/top_right_corner.png", 26, 8 }, + /* Qt::WhatsThisCursor */ + { DATADIR "/wayland/top_right_corner.png", 26, 8 }, + /* Qt::BusyCursor */ + { DATADIR "/wayland/top_right_corner.png", 26, 8 }, + /* Qt::OpenHandCursor */ + { DATADIR "/wayland/hand1.png", 18, 11 }, + /* Qt::ClosedHandCursor */ + { DATADIR "/wayland/grabbing.png", 20, 17 }, + /* Qt::DragCopyCursor */ + { DATADIR "/wayland/dnd-copy.png", 13, 13 }, + /* Qt::DragMoveCursor */ + { DATADIR "/wayland/dnd-move.png", 13, 13 }, + /* Qt::DragLinkCursor */ + { DATADIR "/wayland/dnd-link.png", 13, 13 }, +}; + +QWaylandCursor::QWaylandCursor(QWaylandScreen *screen) + : QPlatformCursor(screen) + , mBuffer(0) + , mDisplay(screen->display()) +{ +} + +void QWaylandCursor::changeCursor(QCursor *cursor, QWidget *widget) +{ + const struct pointer_image *p; + + if (widget == NULL) + return; + + p = NULL; + + switch (cursor->shape()) { + case Qt::ArrowCursor: + p = &pointer_images[cursor->shape()]; + break; + case Qt::UpArrowCursor: + case Qt::CrossCursor: + case Qt::WaitCursor: + break; + case Qt::IBeamCursor: + p = &pointer_images[cursor->shape()]; + break; + case Qt::SizeVerCursor: /* 5 */ + case Qt::SizeHorCursor: + case Qt::SizeBDiagCursor: + case Qt::SizeFDiagCursor: + case Qt::SizeAllCursor: + case Qt::BlankCursor: /* 10 */ + break; + case Qt::SplitVCursor: + case Qt::SplitHCursor: + case Qt::PointingHandCursor: + p = &pointer_images[cursor->shape()]; + break; + case Qt::ForbiddenCursor: + case Qt::WhatsThisCursor: /* 15 */ + case Qt::BusyCursor: + break; + case Qt::OpenHandCursor: + case Qt::ClosedHandCursor: + case Qt::DragCopyCursor: + case Qt::DragMoveCursor: /* 20 */ + case Qt::DragLinkCursor: + p = &pointer_images[cursor->shape()]; + break; + + default: + case Qt::BitmapCursor: + break; + } + + if (!p) { + p = &pointer_images[0]; + qWarning("unhandled cursor %d", cursor->shape()); + } + + QImageReader reader(p->filename); + + if (mBuffer == NULL || mBuffer->size() != reader.size()) { + if (mBuffer) + delete mBuffer; + + mBuffer = new QWaylandShmBuffer(mDisplay, reader.size(), + QImage::Format_ARGB32); + } + + reader.read(mBuffer->image()); + + mDisplay->setCursor(mBuffer, p->hotspot_x, p->hotspot_y); +} + +void QWaylandDisplay::setCursor(QWaylandBuffer *buffer, int32_t x, int32_t y) +{ + /* Qt doesn't tell us which input device we should set the cursor + * for, so set it for all devices. */ + for (int i = 0; i < mInputDevices.count(); i++) { + QWaylandInputDevice *inputDevice = mInputDevices.at(i); + inputDevice->attach(buffer, x, y); + } +} diff --git a/src/plugins/platforms/wayland/qwaylandcursor.h b/src/plugins/platforms/wayland/qwaylandcursor.h new file mode 100644 index 0000000..19e6047 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandcursor.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDCURSOR_H +#define QWAYLANDCURSOR_H + +#include <QtGui/QPlatformCursor> + +class QWaylandShmBuffer; +class QWaylandDisplay; +class QWaylandScreen; + +class QWaylandCursor : QPlatformCursor { +public: + QWaylandCursor(QWaylandScreen *screen); + + void changeCursor(QCursor *cursor, QWidget *widget); + QWaylandShmBuffer *mBuffer; + QWaylandDisplay *mDisplay; +}; + +#endif // QWAYLANDCURSOR_H diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp new file mode 100644 index 0000000..70713ec --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylanddisplay.h" + +#include "qwaylandwindow.h" +#include "qwaylandscreen.h" +#include "qwaylandcursor.h" +#include "qwaylandinputdevice.h" + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> + +extern "C" { +#include <xf86drm.h> +} + +struct wl_surface *QWaylandDisplay::createSurface() +{ + return wl_compositor_create_surface(mCompositor); +} + +struct wl_buffer *QWaylandDisplay::createShmBuffer(int fd, + int width, int height, + uint32_t stride, + struct wl_visual *visual) +{ + return wl_shm_create_buffer(mShm, fd, width, height, stride, visual); +} + +struct wl_buffer *QWaylandDisplay::createDrmBuffer(int name, + int width, int height, + uint32_t stride, + struct wl_visual *visual) +{ + return wl_drm_create_buffer(mDrm, name, width, height, stride, visual); +} + +struct wl_visual *QWaylandDisplay::rgbVisual() +{ + return wl_display_get_rgb_visual(mDisplay); +} + +struct wl_visual *QWaylandDisplay::argbVisual() +{ + return wl_display_get_argb_visual(mDisplay); +} + +struct wl_visual *QWaylandDisplay::argbPremultipliedVisual() +{ + return wl_display_get_premultiplied_argb_visual(mDisplay); +} + +void QWaylandDisplay::drmHandleDevice(void *data, + struct wl_drm *drm, const char *device) +{ + Q_UNUSED(drm); + QWaylandDisplay *qwd = (QWaylandDisplay *) data; + drm_magic_t magic; + + qwd->mDeviceName = strdup(device); + + qwd->mFd = open(qwd->mDeviceName, O_RDWR); + if (qwd->mFd < 0) { + qWarning("drm open failed: %m"); + return; + } + + if (drmGetMagic(qwd->mFd, &magic)) { + qWarning("DRI2: failed to get drm magic"); + return; + } + + wl_drm_authenticate(qwd->mDrm, magic); +} + +void QWaylandDisplay::drmHandleAuthenticated(void *data, struct wl_drm *drm) +{ + Q_UNUSED(drm); + QWaylandDisplay *qwd = (QWaylandDisplay *) data; + EGLint major, minor; + const char *extensions; + + qwd->mEglDisplay = eglGetDRMDisplayMESA(qwd->mFd); + if (qwd->mEglDisplay == NULL) { + qWarning("failed to create display"); + return; + } + + if (!eglInitialize(qwd->mEglDisplay, &major, &minor)) { + qWarning("failed to initialize display"); + qwd->mEglDisplay = NULL; + return; + } + + extensions = eglQueryString(qwd->mEglDisplay, EGL_EXTENSIONS); + if (!strstr(extensions, "EGL_KHR_surfaceless_gles2")) { + qWarning("EGL_KHR_surfaceless_opengles2 not available"); + eglTerminate(qwd->mEglDisplay); + qwd->mEglDisplay = NULL; + return; + } +} + +const struct wl_drm_listener QWaylandDisplay::drmListener = { + QWaylandDisplay::drmHandleDevice, + QWaylandDisplay::drmHandleAuthenticated +}; + +void QWaylandDisplay::shellHandleConfigure(void *data, struct wl_shell *shell, + uint32_t time, uint32_t edges, + struct wl_surface *surface, + int32_t width, int32_t height) +{ + Q_UNUSED(data); + Q_UNUSED(shell); + Q_UNUSED(time); + Q_UNUSED(edges); + QWaylandWindow *ww = (QWaylandWindow *) wl_surface_get_user_data(surface); + + ww->configure(time, edges, 0, 0, width, height); +} + +const struct wl_shell_listener QWaylandDisplay::shellListener = { + QWaylandDisplay::shellHandleConfigure, +}; + +void QWaylandDisplay::outputHandleGeometry(void *data, + struct wl_output *output, + int32_t x, int32_t y, + int32_t width, int32_t height) +{ + QWaylandDisplay *waylandDisplay = (QWaylandDisplay *) data; + + QRect outputRect = QRect(x, y, width, height); + waylandDisplay->createNewScreen(output, outputRect); +} + +const struct wl_output_listener QWaylandDisplay::outputListener = { + QWaylandDisplay::outputHandleGeometry +}; + +void QWaylandDisplay::displayHandleGlobal(struct wl_display *display, + uint32_t id, + const char *interface, + uint32_t version, void *data) +{ + Q_UNUSED(version); + QWaylandDisplay *qwd = (QWaylandDisplay *) data; + + if (strcmp(interface, "compositor") == 0) { + qwd->mCompositor = wl_compositor_create(display, id); + } else if (strcmp(interface, "drm") == 0) { + qwd->mDrm = wl_drm_create(display, id); + wl_drm_add_listener(qwd->mDrm, &drmListener, qwd); + } else if (strcmp(interface, "shm") == 0) { + qwd->mShm = wl_shm_create(display, id); + } else if (strcmp(interface, "shell") == 0) { + qwd->mShell = wl_shell_create(display, id); + wl_shell_add_listener(qwd->mShell, &shellListener, qwd); + } else if (strcmp(interface, "output") == 0) { + struct wl_output *output = wl_output_create(display, id); + wl_output_add_listener(output, &outputListener, qwd); + } else if (strcmp(interface, "input_device") == 0) { + QWaylandInputDevice *inputDevice = + new QWaylandInputDevice(display, id); + qwd->mInputDevices.append(inputDevice); + } +} + +static void roundtripCallback(void *data) +{ + bool *done = (bool *) data; + + *done = true; +} + +static void forceRoundtrip(struct wl_display *display) +{ + bool done; + + wl_display_sync_callback(display, roundtripCallback, &done); + wl_display_iterate(display, WL_DISPLAY_WRITABLE); + done = false; + while (!done) + wl_display_iterate(display, WL_DISPLAY_READABLE); +} + +static const char *socket_name = NULL; + +void QWaylandDisplay::eventDispatcher(void) +{ + wl_display_iterate(mDisplay, WL_DISPLAY_READABLE); +} + +int +QWaylandDisplay::sourceUpdate(uint32_t mask, void *data) +{ + QWaylandDisplay *qwd = (QWaylandDisplay *) data; + + /* FIXME: We get a callback here when we ask wl_display for the + * fd, but at that point we don't have the socket notifier as we + * need the fd to create that. We'll probably need to split that + * API into get_fd and set_update_func functions. */ + if (qwd->mWriteNotifier == NULL) + return 0; + + qwd->mWriteNotifier->setEnabled(mask & WL_DISPLAY_WRITABLE); + + return 0; +} + +void QWaylandDisplay::flushRequests(void) +{ + wl_display_iterate(mDisplay, WL_DISPLAY_WRITABLE); +} + +QWaylandDisplay::QWaylandDisplay(void) + : mWriteNotifier(0) +{ + mDisplay = wl_display_connect(socket_name); + if (mDisplay == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return; + } + + wl_display_add_global_listener(mDisplay, + QWaylandDisplay::displayHandleGlobal, this); + + /* Process connection events. */ + wl_display_iterate(mDisplay, WL_DISPLAY_READABLE); + if (!mShm || !mDeviceName) + forceRoundtrip(mDisplay); + + /* Force a roundtrip to finish the drm authentication so we + * initialize EGL before proceeding */ + forceRoundtrip(mDisplay); + if (mEglDisplay == NULL) + qDebug("EGL not available"); + else + qDebug("EGL initialized"); + + int fd = wl_display_get_fd(mDisplay, sourceUpdate, this); + mReadNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(mReadNotifier, + SIGNAL(activated(int)), this, SLOT(eventDispatcher())); + + mWriteNotifier = new QSocketNotifier(fd, QSocketNotifier::Write, this); + connect(mWriteNotifier, + SIGNAL(activated(int)), this, SLOT(flushRequests())); + mWriteNotifier->setEnabled(false); +} + +QWaylandDisplay::~QWaylandDisplay(void) +{ + close(mFd); + wl_display_destroy(mDisplay); +} + +void QWaylandDisplay::createNewScreen(struct wl_output *output, QRect geometry) +{ + QWaylandScreen *waylandScreen = new QWaylandScreen(this,output,geometry); + mScreens.append(waylandScreen); +} + +void QWaylandDisplay::syncCallback(wl_display_sync_func_t func, void *data) +{ + wl_display_sync_callback(mDisplay, func, data); +} + +void QWaylandDisplay::frameCallback(wl_display_frame_func_t func, void *data) +{ + wl_display_frame_callback(mDisplay, func, data); +} diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.h b/src/plugins/platforms/wayland/qwaylanddisplay.h new file mode 100644 index 0000000..f179713 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylanddisplay.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDDISPLAY_H +#define QWAYLANDDISPLAY_H + +#include <QtCore/QObject> +#include <QtCore/QRect> + +#include <wayland-client.h> + +#define MESA_EGL_NO_X11_HEADERS +#define EGL_EGLEXT_PROTOTYPES +#include <EGL/egl.h> +#include <EGL/eglext.h> + +class QWaylandInputDevice; +class QSocketNotifier; +class QWaylandBuffer; +class QPlatformScreen; +class QWaylandScreen; + +class QWaylandDisplay : public QObject { + Q_OBJECT + +public: + QWaylandDisplay(void); + ~QWaylandDisplay(void); + + void createNewScreen(struct wl_output *output, QRect geometry); + QList<QPlatformScreen *> screens() const { return mScreens; } + struct wl_surface *createSurface(); + struct wl_buffer *createShmBuffer(int fd, int width, int height, + uint32_t stride, + struct wl_visual *visual); + struct wl_buffer *createDrmBuffer(int name, int width, int height, + uint32_t stride, + struct wl_visual *visual); + struct wl_visual *rgbVisual(); + struct wl_visual *argbVisual(); + struct wl_visual *argbPremultipliedVisual(); + EGLDisplay eglDisplay() { return mEglDisplay; } + + void setCursor(QWaylandBuffer *buffer, int32_t x, int32_t y); + + void syncCallback(wl_display_sync_func_t func, void *data); + void frameCallback(wl_display_frame_func_t func, void *data); + +public slots: + void eventDispatcher(void); + void flushRequests(void); + +private: + struct wl_display *mDisplay; + struct wl_compositor *mCompositor; + struct wl_drm *mDrm; + struct wl_shm *mShm; + struct wl_shell *mShell; + char *mDeviceName; + int mFd; + QList<QPlatformScreen *> mScreens; + QList<QWaylandInputDevice *> mInputDevices; + QSocketNotifier *mReadNotifier; + QSocketNotifier *mWriteNotifier; + EGLDisplay mEglDisplay; + + static void displayHandleGlobal(struct wl_display *display, + uint32_t id, + const char *interface, + uint32_t version, void *data); + + static void drmHandleDevice(void *data, + struct wl_drm *drm, const char *device); + static void drmHandleAuthenticated(void *data, struct wl_drm *drm); + + static void outputHandleGeometry(void *data, + struct wl_output *output, + int32_t x, int32_t y, + int32_t width, int32_t height); + + static void shellHandleConfigure(void *data, struct wl_shell *shell, + uint32_t time, uint32_t edges, + struct wl_surface *surface, + int32_t width, int32_t height); + + static int sourceUpdate(uint32_t mask, void *data); + + static const struct wl_drm_listener drmListener; + static const struct wl_output_listener outputListener; + static const struct wl_shell_listener shellListener; +}; + +#endif // QWAYLANDDISPLAY_H diff --git a/src/plugins/platforms/wayland/qwaylanddrmsurface.cpp b/src/plugins/platforms/wayland/qwaylanddrmsurface.cpp new file mode 100644 index 0000000..76c8c33 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylanddrmsurface.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwaylanddrmsurface.h" + +#include "qwaylanddisplay.h" +#include "qwaylandwindow.h" +#include "qwaylandscreen.h" + +#include <QtCore/qdebug.h> +#include <QtGui/private/qapplication_p.h> +#include <QtOpenGL/private/qgl_p.h> +#include <QtGui/private/qpaintengine_p.h> + +#include <wayland-client.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/mman.h> + +QT_BEGIN_NAMESPACE + +/* + * Shared DRM surface for GL based drawing + */ +QWaylandDrmBuffer::QWaylandDrmBuffer(QWaylandDisplay *display, + const QSize &size, QImage::Format format) + : mDisplay(display) + , mSize(size) +{ + struct wl_visual *visual; + EGLint name, stride; + EGLint imageAttribs[] = { + EGL_WIDTH, size.width(), + EGL_HEIGHT, size.height(), + EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA, + EGL_DRM_BUFFER_USE_MESA, EGL_DRM_BUFFER_USE_SCANOUT_MESA, + EGL_NONE + }; + + mImage = eglCreateDRMImageMESA(mDisplay->eglDisplay(), imageAttribs); + + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage); + glGenTextures(1, &mTexture); + glBindTexture(GL_TEXTURE_2D, mTexture); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage); + + eglExportDRMImageMESA(mDisplay->eglDisplay(), + mImage, &name, NULL, &stride); + + switch (format) { + case QImage::Format_ARGB32: + visual = display->argbVisual(); + break; + case QImage::Format_ARGB32_Premultiplied: + visual = display->argbPremultipliedVisual(); + break; + default: + qDebug("unsupported buffer format %d requested\n", format); + visual = display->argbVisual(); + break; + } + + mBuffer = display->createDrmBuffer(name, size.width(), size.height(), + stride, visual); +} + +QWaylandDrmBuffer::~QWaylandDrmBuffer(void) +{ + glDeleteTextures(1, &mTexture); + eglDestroyImageKHR(mDisplay->eglDisplay(), mImage); + wl_buffer_destroy(mBuffer); +} + +void QWaylandDrmBuffer::bindToCurrentFbo() +{ + Q_ASSERT(QPlatformGLContext::currentContext()); + glBindTexture(GL_TEXTURE_2D, mTexture); + glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, mTexture, 0); + QT_CHECK_GLERROR(); +} + +QWaylandPaintDevice::QWaylandPaintDevice(QWaylandDisplay *display, QWindowSurface *windowSurface, QPlatformGLContext *context) + : QGLPaintDevice() + , mDisplay(display) + , mPlatformGLContext(context) + , mWindowSurface(windowSurface) + , mBufferList(1) + , mCurrentPaintBuffer(0) + , mDepthStencil(0) +{ + for (int i = 0; i < mBufferList.size(); i++) { + mBufferList[i] = 0; + } + + mPlatformGLContext->makeCurrent(); + glGenFramebuffers(1, &m_thisFBO); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_thisFBO); + + if (windowSurface->size().isValid()) + resize(windowSurface->size()); +} + +QWaylandPaintDevice::~QWaylandPaintDevice() +{ + for(int i = 0; i < mBufferList.size(); i++) { + delete mBufferList[i]; + } + glDeleteRenderbuffers(1,&mDepthStencil); + glDeleteFramebuffers(1,&m_thisFBO); +} + +QSize QWaylandPaintDevice::size() const +{ + return mSize; +} +QGLContext *QWaylandPaintDevice::context() const +{ + return QGLContext::fromPlatformGLContext(mPlatformGLContext); +} +QPaintEngine *QWaylandPaintDevice::paintEngine() const +{ + return qt_qgl_paint_engine(); +} + +void QWaylandPaintDevice::beginPaint() +{ + QGLPaintDevice::beginPaint(); + currentDrmBuffer()->bindToCurrentFbo(); +} + +bool QWaylandPaintDevice::isFlipped()const +{ + return true; +} + +void QWaylandPaintDevice::resize(const QSize &size) +{ + QImage::Format format = QPlatformScreen::platformScreenForWidget(mWindowSurface->window())->format(); + mPlatformGLContext->makeCurrent(); + mSize = size; + for (int i = 0; i < mBufferList.size(); i++) { + if (!mBufferList.at(i) || mBufferList.at(i)->size() != size) { + delete mBufferList[i]; + mBufferList[i] = new QWaylandDrmBuffer(mDisplay, size, format); + } + } + + glDeleteRenderbuffers(1,&mDepthStencil); + glGenRenderbuffers(1,&mDepthStencil); + glBindRenderbuffer(GL_RENDERBUFFER_EXT,mDepthStencil); + glRenderbufferStorage(GL_RENDERBUFFER_EXT, + GL_DEPTH24_STENCIL8_EXT, size.width(), size.height()); + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, mDepthStencil); + glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, mDepthStencil); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); + + switch(status) { + case GL_NO_ERROR: + case GL_FRAMEBUFFER_COMPLETE_EXT: + qDebug() << "fbo ok"; + break; + default: + qDebug() <<"QWaylandDrmBuffer error: "<< status; + break; + } + QT_CHECK_GLERROR(); +} + +QWaylandDrmBuffer *QWaylandPaintDevice::currentDrmBuffer() const +{ + return mBufferList[mCurrentPaintBuffer]; +} +QWaylandDrmBuffer *QWaylandPaintDevice::currentDrmBufferAndSwap() +{ + QWaylandDrmBuffer *currentDrmBuffer = mBufferList[mCurrentPaintBuffer]; + mCurrentPaintBuffer = (mCurrentPaintBuffer +1) % mBufferList.size(); + return currentDrmBuffer; +} + +QWaylandDrmWindowSurface::QWaylandDrmWindowSurface(QWidget *window) + : QWindowSurface(window) + , mDisplay(QWaylandScreen::waylandScreenFromWidget(window)->display()) + , mPaintDevice(new QWaylandPaintDevice(mDisplay, this,window->platformWindow()->glContext())) +{ + +} + +QWaylandDrmWindowSurface::~QWaylandDrmWindowSurface() +{ + delete mPaintDevice; +} + +void QWaylandDrmWindowSurface::beginPaint(const QRegion &) +{ + window()->platformWindow()->glContext()->makeCurrent(); +} + +QPaintDevice *QWaylandDrmWindowSurface::paintDevice() +{ + return mPaintDevice; +} + +void QWaylandDrmWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(offset); + QWaylandWindow *ww = (QWaylandWindow *) widget->platformWindow(); + + ww->attach(mPaintDevice->currentDrmBufferAndSwap()); + + QVector<QRect> rects = region.rects(); + for (int i = 0; i < rects.size(); i++) { + QRect r = rects.at(i); + wl_surface_damage(ww->surface(), + r.x(), r.y(), r.width(), r.height()); + } +} + +void QWaylandDrmWindowSurface::resize(const QSize &requestedSize) +{ + QWindowSurface::resize(requestedSize); + QWaylandWindow *ww = (QWaylandWindow *) window()->platformWindow(); + + ww->glContext()->makeCurrent(); + + mPaintDevice->resize(requestedSize); + ww->attach(mPaintDevice->currentDrmBuffer()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/qwaylanddrmsurface.h b/src/plugins/platforms/wayland/qwaylanddrmsurface.h new file mode 100644 index 0000000..eafea13 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylanddrmsurface.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDDRMSURFACE_H +#define QWAYLANDDRMSURFACE_H + +#include "qwaylanddisplay.h" +#include "qwaylandbuffer.h" + +#include <QtGui/private/qwindowsurface_p.h> +#include <QtCore/QVarLengthArray> +#include <QtOpenGL/private/qglpaintdevice_p.h> + +#define GL_GLEXT_PROTOTYPES +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#define QT_RESET_GLERROR() \ +{ \ + while (glGetError() != GL_NO_ERROR) {} \ +} +#define QT_CHECK_GLERROR() \ +{ \ + GLenum err = glGetError(); \ + if (err != GL_NO_ERROR) { \ + qDebug("[%s line %d] GL Error: 0x%x", \ + __FILE__, __LINE__, (int)err); \ + } \ +} + +class QWaylandDrmBuffer : public QWaylandBuffer { +public: + QWaylandDrmBuffer(QWaylandDisplay *display, + const QSize &size, QImage::Format format); + ~QWaylandDrmBuffer(); + + void bindToCurrentFbo(); + QSize size() const { return mSize; } + +private: + EGLImageKHR mImage; + QWaylandDisplay *mDisplay; + QSize mSize; + GLuint mTexture; + +}; + +class QWaylandPaintDevice : public QGLPaintDevice +{ +public: + QWaylandPaintDevice(QWaylandDisplay *display, QWindowSurface *windowSurface, QPlatformGLContext *context); + ~QWaylandPaintDevice(); + + QSize size() const; + QGLContext *context() const; + QPaintEngine *paintEngine() const; + + void beginPaint(); + + bool isFlipped()const; + + void resize(const QSize &size); + + QWaylandDrmBuffer *currentDrmBuffer() const; + QWaylandDrmBuffer *currentDrmBufferAndSwap(); + +private: + QWaylandDisplay *mDisplay; + QPlatformGLContext *mPlatformGLContext; + QWindowSurface *mWindowSurface; + QVarLengthArray<QWaylandDrmBuffer *> mBufferList; + int mCurrentPaintBuffer; + GLuint mDepthStencil; + QSize mSize; + +}; + +class QWaylandDrmWindowSurface : public QWindowSurface +{ +public: + QWaylandDrmWindowSurface(QWidget *window); + ~QWaylandDrmWindowSurface(); + + void beginPaint(const QRegion &); + + QPaintDevice *paintDevice(); + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + void resize(const QSize &size); + +private: + + QWaylandDisplay *mDisplay; + QWaylandPaintDevice *mPaintDevice; +}; + +#endif // QWAYLANDDRMSURFACE_H diff --git a/src/plugins/platforms/wayland/qwaylandglcontext.cpp b/src/plugins/platforms/wayland/qwaylandglcontext.cpp new file mode 100644 index 0000000..4bf68c4 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandglcontext.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandglcontext.h" + +#include "qwaylanddisplay.h" +#include "qwaylandwindow.h" +#include "qwaylanddrmsurface.h" + +#include <QtGui/QPlatformGLContext> +#include <QtGui/QPlatformWindowFormat> + +#include <unistd.h> +#include <fcntl.h> + +extern "C" { +#include <xf86drm.h> +} + +Q_GLOBAL_STATIC(QMutex,qt_defaultSharedContextMutex); + +EGLint QWaylandGLContext::contextAttibutes[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE +}; + +QWaylandGLContext::QWaylandGLContext(QWaylandDisplay *wd, const QPlatformWindowFormat &format) + : QPlatformGLContext() + , mFormat(format) + , mDisplay(wd) +{ + QPlatformGLContext *sharePlatformContext; + if (format.useDefaultSharedContext()) { + if (!QPlatformGLContext::defaultSharedContext()) { + if (qt_defaultSharedContextMutex()->tryLock()){ + createDefaultSharedContex(wd); + qt_defaultSharedContextMutex()->unlock(); + } else { + qt_defaultSharedContextMutex()->lock(); //wait to the the shared context is created + qt_defaultSharedContextMutex()->unlock(); + } + } + sharePlatformContext = QPlatformGLContext::defaultSharedContext(); + } else { + sharePlatformContext = format.sharedGLContext(); + } + mFormat.setSharedContext(sharePlatformContext); + EGLContext shareEGLContext = EGL_NO_CONTEXT; + if (sharePlatformContext) + shareEGLContext = static_cast<const QWaylandGLContext*>(sharePlatformContext)->mContext; + + eglBindAPI(EGL_OPENGL_ES_API); + mContext = eglCreateContext(mDisplay->eglDisplay(), NULL, + shareEGLContext, contextAttibutes); + + mFormat.setAccum(false); + mFormat.setAlphaBufferSize(8); + mFormat.setRedBufferSize(8); + mFormat.setGreenBufferSize(8); + mFormat.setBlueBufferSize(8); + mFormat.setDepth(false); +// mFormat.setDepthBufferSize(8); + mFormat.setStencil(false); +// mFormat.setStencilBufferSize(24); +// mFormat.setSampleBuffers(false); + +} + +QWaylandGLContext::QWaylandGLContext() + : QPlatformGLContext() + , mDisplay(0) + , mContext(EGL_NO_CONTEXT) +{ } + +QWaylandGLContext::~QWaylandGLContext() +{ + eglDestroyContext(mDisplay->eglDisplay(),mContext); +} + +void QWaylandGLContext::makeCurrent() +{ + QPlatformGLContext::makeCurrent(); + eglMakeCurrent(mDisplay->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, mContext); +} + +void QWaylandGLContext::doneCurrent() +{ + QPlatformGLContext::doneCurrent(); + eglMakeCurrent(mDisplay->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + +void QWaylandGLContext::swapBuffers() +{ +} + +void *QWaylandGLContext::getProcAddress(const QString &string) +{ + return (void *) eglGetProcAddress(string.toLatin1().data()); +} + +void QWaylandGLContext::createDefaultSharedContex(QWaylandDisplay *display) +{ + QWaylandGLContext *defaultSharedContext = new QWaylandGLContext; + defaultSharedContext->mDisplay = display; + defaultSharedContext->mContext = eglCreateContext(mDisplay->eglDisplay(), NULL, + EGL_NO_CONTEXT, contextAttibutes); + QPlatformGLContext::setDefaultSharedContext(defaultSharedContext); +} + diff --git a/src/plugins/platforms/wayland/qwaylandglcontext.h b/src/plugins/platforms/wayland/qwaylandglcontext.h new file mode 100644 index 0000000..d9ba323 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandglcontext.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDGLCONTEXT_H +#define QWAYLANDGLCONTEXT_H + +#include <QtGui/QPlatformGLContext> + +#include <QtCore/QMutex> +class QWaylandDisplay; +class QWaylandWindow; +class QWaylandDrmWindowSurface; + +#define MESA_EGL_NO_X11_HEADERS +#define EGL_EGLEXT_PROTOTYPES +#include <EGL/egl.h> +#include <EGL/eglext.h> + +class QWaylandGLContext : public QPlatformGLContext { +public: + QWaylandGLContext(QWaylandDisplay *wd, const QPlatformWindowFormat &format); + ~QWaylandGLContext(); + void makeCurrent(); + void doneCurrent(); + void swapBuffers(); + void* getProcAddress(const QString&); + QPlatformWindowFormat platformWindowFormat() const { return mFormat; } + +private: + QPlatformWindowFormat mFormat; + QWaylandDisplay *mDisplay; + + static EGLint contextAttibutes[]; + EGLContext mContext; + + void createDefaultSharedContex(QWaylandDisplay *display); + QWaylandGLContext(); + +}; + + +#endif // QWAYLANDGLCONTEXT_H diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp new file mode 100644 index 0000000..03edc07 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandinputdevice.h" + +#include "qwaylandintegration.h" +#include "qwaylandwindow.h" +#include "qwaylandbuffer.h" + +#include <QWindowSystemInterface> + +#include <QtGui/private/qpixmap_raster_p.h> +#include <QtGui/QPlatformWindow> + +#include <unistd.h> +#include <fcntl.h> +#include <X11/extensions/XKBcommon.h> +#include <X11/keysym.h> + +QWaylandInputDevice::QWaylandInputDevice(struct wl_display *display, + uint32_t id) + : mDisplay(display) + , mInputDevice(wl_input_device_create(display, id)) + , mPointerFocus(NULL) + , mKeyboardFocus(NULL) + , mButtons(0) +{ + struct xkb_rule_names names; + + wl_input_device_add_listener(mInputDevice, + &inputDeviceListener, + this); + wl_input_device_set_user_data(mInputDevice, this); + + names.rules = "evdev"; + names.model = "pc105"; + names.layout = "us"; + names.variant = ""; + names.options = ""; + + mXkb = xkb_compile_keymap_from_rules(&names); +} + +void QWaylandInputDevice::inputHandleMotion(void *data, + struct wl_input_device *input_device, + uint32_t time, + int32_t x, int32_t y, + int32_t surface_x, int32_t surface_y) +{ + Q_UNUSED(input_device); + QWaylandInputDevice *inputDevice = (QWaylandInputDevice *) data; + QWaylandWindow *window = inputDevice->mPointerFocus; + + inputDevice->mSurfacePos = QPoint(surface_x, surface_y); + inputDevice->mGlobalPos = QPoint(x, y); + inputDevice->mTime = time; + QWindowSystemInterface::handleMouseEvent(window->widget(), + time, + inputDevice->mSurfacePos, + inputDevice->mGlobalPos, + inputDevice->mButtons); +} + +void QWaylandInputDevice::inputHandleButton(void *data, + struct wl_input_device *input_device, + uint32_t time, uint32_t button, uint32_t state) +{ + Q_UNUSED(input_device); + QWaylandInputDevice *inputDevice = (QWaylandInputDevice *) data; + QWaylandWindow *window = inputDevice->mPointerFocus; + Qt::MouseButton qt_button; + + switch (button) { + case 272: + qt_button = Qt::LeftButton; + break; + case 273: + qt_button = Qt::RightButton; + break; + case 274: + qt_button = Qt::MiddleButton; + break; + default: + return; + } + + if (state) + inputDevice->mButtons |= qt_button; + else + inputDevice->mButtons &= ~qt_button; + + inputDevice->mTime = time; + QWindowSystemInterface::handleMouseEvent(window->widget(), + time, + inputDevice->mSurfacePos, + inputDevice->mGlobalPos, + inputDevice->mButtons); +} + +static Qt::KeyboardModifiers translateModifiers(int s) +{ + const uchar qt_alt_mask = XKB_COMMON_MOD1_MASK; + const uchar qt_meta_mask = XKB_COMMON_MOD4_MASK; + + Qt::KeyboardModifiers ret = 0; + if (s & XKB_COMMON_SHIFT_MASK) + ret |= Qt::ShiftModifier; + if (s & XKB_COMMON_CONTROL_MASK) + ret |= Qt::ControlModifier; + if (s & qt_alt_mask) + ret |= Qt::AltModifier; + if (s & qt_meta_mask) + ret |= Qt::MetaModifier; + + return ret; +} + +static uint32_t translateKey(uint32_t sym, char *string, size_t size) +{ + Q_UNUSED(size); + string[0] = '\0'; + + switch (sym) { + case XK_Escape: return Qt::Key_Escape; + case XK_Tab: return Qt::Key_Tab; + case XK_ISO_Left_Tab: return Qt::Key_Backtab; + case XK_BackSpace: return Qt::Key_Backspace; + case XK_Return: return Qt::Key_Return; + case XK_Insert: return Qt::Key_Insert; + case XK_Delete: return Qt::Key_Delete; + case XK_Clear: return Qt::Key_Delete; + case XK_Pause: return Qt::Key_Pause; + case XK_Print: return Qt::Key_Print; + + case XK_Home: return Qt::Key_Home; + case XK_End: return Qt::Key_End; + case XK_Left: return Qt::Key_Left; + case XK_Up: return Qt::Key_Up; + case XK_Right: return Qt::Key_Right; + case XK_Down: return Qt::Key_Down; + case XK_Prior: return Qt::Key_PageUp; + case XK_Next: return Qt::Key_PageDown; + + case XK_Shift_L: return Qt::Key_Shift; + case XK_Shift_R: return Qt::Key_Shift; + case XK_Shift_Lock: return Qt::Key_Shift; + case XK_Control_L: return Qt::Key_Control; + case XK_Control_R: return Qt::Key_Control; + case XK_Meta_L: return Qt::Key_Meta; + case XK_Meta_R: return Qt::Key_Meta; + case XK_Alt_L: return Qt::Key_Alt; + case XK_Alt_R: return Qt::Key_Alt; + case XK_Caps_Lock: return Qt::Key_CapsLock; + case XK_Num_Lock: return Qt::Key_NumLock; + case XK_Scroll_Lock: return Qt::Key_ScrollLock; + case XK_Super_L: return Qt::Key_Super_L; + case XK_Super_R: return Qt::Key_Super_R; + case XK_Menu: return Qt::Key_Menu; + + default: + string[0] = sym; + string[1] = '\0'; + return toupper(sym); + } +} + +void QWaylandInputDevice::inputHandleKey(void *data, + struct wl_input_device *input_device, + uint32_t time, uint32_t key, uint32_t state) +{ + Q_UNUSED(input_device); + QWaylandInputDevice *inputDevice = (QWaylandInputDevice *) data; + QWaylandWindow *window = inputDevice->mKeyboardFocus; + uint32_t code, sym, level; + Qt::KeyboardModifiers modifiers; + QEvent::Type type; + char s[2]; + + code = key + inputDevice->mXkb->min_key_code; + + level = 0; + if (inputDevice->mModifiers & Qt::ShiftModifier && + XkbKeyGroupWidth(inputDevice->mXkb, code, 0) > 1) + level = 1; + + sym = XkbKeySymEntry(inputDevice->mXkb, code, level, 0); + + modifiers = translateModifiers(inputDevice->mXkb->map->modmap[code]); + + if (state) { + inputDevice->mModifiers |= modifiers; + type = QEvent::KeyPress; + } else { + inputDevice->mModifiers &= ~modifiers; + type = QEvent::KeyRelease; + } + + sym = translateKey(sym, s, sizeof s); + + qWarning("keycode %d, sym %d, string %d, modifiers 0x%x", + code, sym, s[0], (int) inputDevice->mModifiers); + + if (window) { + QWindowSystemInterface::handleKeyEvent(window->widget(), + time, type, sym, + inputDevice->mModifiers, + QString::fromLatin1(s)); + } +} + +void QWaylandInputDevice::inputHandlePointerFocus(void *data, + struct wl_input_device *input_device, + uint32_t time, struct wl_surface *surface, + int32_t x, int32_t y, int32_t sx, int32_t sy) +{ + Q_UNUSED(input_device); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(sx); + Q_UNUSED(sy); + QWaylandInputDevice *inputDevice = (QWaylandInputDevice *) data; + QWaylandWindow *window; + + if (inputDevice->mPointerFocus) { + window = inputDevice->mPointerFocus; + QWindowSystemInterface::handleLeaveEvent(window->widget()); + inputDevice->mPointerFocus = NULL; + } + + if (surface) { + window = (QWaylandWindow *) wl_surface_get_user_data(surface); + QWindowSystemInterface::handleEnterEvent(window->widget()); + inputDevice->mPointerFocus = window; + } + + inputDevice->mTime = time; +} + +void QWaylandInputDevice::inputHandleKeyboardFocus(void *data, + struct wl_input_device *input_device, + uint32_t time, + struct wl_surface *surface, + struct wl_array *keys) +{ + Q_UNUSED(input_device); + Q_UNUSED(time); + Q_UNUSED(keys); + QWaylandInputDevice *inputDevice = (QWaylandInputDevice *) data; + QWaylandWindow *window; + + if (inputDevice->mKeyboardFocus) { + window = inputDevice->mKeyboardFocus; + inputDevice->mKeyboardFocus = NULL; + } + + if (surface) { + window = (QWaylandWindow *) wl_surface_get_user_data(surface); + inputDevice->mKeyboardFocus = window; + } +} + +const struct wl_input_device_listener QWaylandInputDevice::inputDeviceListener = { + QWaylandInputDevice::inputHandleMotion, + QWaylandInputDevice::inputHandleButton, + QWaylandInputDevice::inputHandleKey, + QWaylandInputDevice::inputHandlePointerFocus, + QWaylandInputDevice::inputHandleKeyboardFocus, +}; + +void QWaylandInputDevice::attach(QWaylandBuffer *buffer, int x, int y) +{ + wl_input_device_attach(mInputDevice, mTime, buffer->buffer(), x, y); +} diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.h b/src/plugins/platforms/wayland/qwaylandinputdevice.h new file mode 100644 index 0000000..2328db8 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandinputdevice.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDINPUTDEVICE_H +#define QWAYLANDINPUTDEVICE_H + +#include "qwaylandwindow.h" + +#include <QSocketNotifier> +#include <QObject> +#include <QtGui/QPlatformIntegration> +#include <QtGui/QPlatformScreen> + +#include <wayland-client.h> + +QT_BEGIN_NAMESPACE + +class QWaylandWindow; + +class QWaylandInputDevice { +public: + QWaylandInputDevice(struct wl_display *display, uint32_t id); + void attach(QWaylandBuffer *buffer, int x, int y); + +private: + struct wl_display *mDisplay; + struct wl_input_device *mInputDevice; + QWaylandWindow *mPointerFocus; + QWaylandWindow *mKeyboardFocus; + static const struct wl_input_device_listener inputDeviceListener; + Qt::MouseButtons mButtons; + QPoint mSurfacePos; + QPoint mGlobalPos; + struct xkb_desc *mXkb; + Qt::KeyboardModifiers mModifiers; + uint32_t mTime; + + static void inputHandleMotion(void *data, + struct wl_input_device *input_device, + uint32_t time, + int32_t x, int32_t y, + int32_t sx, int32_t sy); + static void inputHandleButton(void *data, + struct wl_input_device *input_device, + uint32_t time, uint32_t button, uint32_t state); + static void inputHandleKey(void *data, + struct wl_input_device *input_device, + uint32_t time, uint32_t key, uint32_t state); + static void inputHandlePointerFocus(void *data, + struct wl_input_device *input_device, + uint32_t time, struct wl_surface *surface, + int32_t x, int32_t y, int32_t sx, int32_t sy); + static void inputHandleKeyboardFocus(void *data, + struct wl_input_device *input_device, + uint32_t time, + struct wl_surface *surface, + struct wl_array *keys); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp new file mode 100644 index 0000000..02bc680 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandintegration.h" + +#include "qwaylanddisplay.h" +#include "qwaylandshmsurface.h" +#include "qwaylanddrmsurface.h" +#include "qwaylandwindow.h" + +#include "qfontconfigdatabase.h" + +#include <QtGui/QWindowSystemInterface> +#include <QtGui/QPlatformCursor> +#include <QtGui/QPlatformWindowFormat> + +#include <QtGui/private/qpixmap_raster_p.h> +#include <QtOpenGL/private/qpixmapdata_gl_p.h> + +QWaylandIntegration::QWaylandIntegration(bool useOpenGL) + : mFontDb(new QFontconfigDatabase()) + , mDisplay(new QWaylandDisplay()) + , mUseOpenGL(useOpenGL) +{ +} + +QList<QPlatformScreen *> +QWaylandIntegration::screens() const +{ + return mDisplay->screens(); +} + +QPixmapData *QWaylandIntegration::createPixmapData(QPixmapData::PixelType type) const +{ + if (mUseOpenGL) + return new QGLPixmapData(type); + return new QRasterPixmapData(type); +} + + + +QPlatformWindow *QWaylandIntegration::createPlatformWindow(QWidget *widget, WId winId) const +{ + Q_UNUSED(winId); + return new QWaylandWindow(widget); +} + +QWindowSurface *QWaylandIntegration::createWindowSurface(QWidget *widget, WId winId) const +{ + Q_UNUSED(winId); + Q_UNUSED(winId); + + if (mUseOpenGL) + return new QWaylandDrmWindowSurface(widget); + return new QWaylandShmWindowSurface(widget); +} + +QPlatformFontDatabase *QWaylandIntegration::fontDatabase() const +{ + return mFontDb; +} diff --git a/src/plugins/platforms/wayland/qwaylandintegration.h b/src/plugins/platforms/wayland/qwaylandintegration.h new file mode 100644 index 0000000..d707612 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandintegration.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMINTEGRATION_WAYLAND_H +#define QPLATFORMINTEGRATION_WAYLAND_H + +#include <QtGui/QPlatformIntegration> + +QT_BEGIN_NAMESPACE + +class QWaylandBuffer; +class QWaylandDisplay; + +class QWaylandIntegration : public QPlatformIntegration +{ +public: + QWaylandIntegration(bool useOpenGL = false); + + QPixmapData *createPixmapData(QPixmapData::PixelType type) const; + QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId) const; + QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const; + + QList<QPlatformScreen *> screens() const; + + QPlatformFontDatabase *fontDatabase() const; + +private: + QPlatformFontDatabase *mFontDb; + QWaylandDisplay *mDisplay; + bool mUseOpenGL; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/qwaylandscreen.cpp b/src/plugins/platforms/wayland/qwaylandscreen.cpp new file mode 100644 index 0000000..aa1083f --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandscreen.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandscreen.h" + +#include "qwaylanddisplay.h" +#include "qwaylandcursor.h" + +QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, struct wl_output *output, QRect geometry) + : QPlatformScreen() + , mWaylandDisplay(waylandDisplay) + , mOutput(output) + , mGeometry(geometry) + , mDepth(32) + , mFormat(QImage::Format_ARGB32_Premultiplied) + , mWaylandCursor(new QWaylandCursor(this)) +{ +} + +QWaylandScreen::~QWaylandScreen() +{ + delete mWaylandCursor; +} + +QWaylandDisplay * QWaylandScreen::display() const +{ + return mWaylandDisplay; +} + +QRect QWaylandScreen::geometry() const +{ + return mGeometry; +} + +int QWaylandScreen::depth() const +{ + return mDepth; +} + +QImage::Format QWaylandScreen::format() const +{ + return mFormat; +} + +QWaylandScreen * QWaylandScreen::waylandScreenFromWidget(QWidget *widget) +{ + QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWidget(widget); + return static_cast<QWaylandScreen *>(platformScreen); +} diff --git a/src/plugins/platforms/wayland/qwaylandscreen.h b/src/plugins/platforms/wayland/qwaylandscreen.h new file mode 100644 index 0000000..368859f --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandscreen.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDSCREEN_H +#define QWAYLANDSCREEN_H + +#include <QtGui/QPlatformScreen> + +class QWaylandDisplay; +class QWaylandCursor; + +class QWaylandScreen : public QPlatformScreen +{ +public: + QWaylandScreen(QWaylandDisplay *waylandDisplay, struct wl_output *output, QRect geometry); + ~QWaylandScreen(); + + QWaylandDisplay *display() const; + + QRect geometry() const; + int depth() const; + QImage::Format format() const; + + static QWaylandScreen *waylandScreenFromWidget(QWidget *widget); + +private: + QWaylandDisplay *mWaylandDisplay; + struct wl_output *mOutput; + QRect mGeometry; + int mDepth; + QImage::Format mFormat; + QSize mPhysicalSize; + QWaylandCursor *mWaylandCursor; +}; + +#endif // QWAYLANDSCREEN_H diff --git a/src/plugins/platforms/wayland/qwaylandshmsurface.cpp b/src/plugins/platforms/wayland/qwaylandshmsurface.cpp new file mode 100644 index 0000000..83bb993 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandshmsurface.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwaylandshmsurface.h" + +#include <QtCore/qdebug.h> +#include <QtGui/private/qapplication_p.h> + +#include "qwaylanddisplay.h" +#include "qwaylandwindow.h" +#include "qwaylandscreen.h" + +#include <wayland-client.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/mman.h> + +QT_BEGIN_NAMESPACE + +QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, + const QSize &size, QImage::Format format) +{ + int stride = size.width() * 4; + int alloc = stride * size.height(); + char filename[] = "/tmp/wayland-shm-XXXXXX"; + int fd = mkstemp(filename); + if (fd < 0) + qWarning("open %s failed: %s", filename, strerror(errno)); + if (ftruncate(fd, alloc) < 0) { + qWarning("ftruncate failed: %s", strerror(errno)); + close(fd); + return; + } + uchar *data = (uchar *) + mmap(NULL, alloc, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + unlink(filename); + + if (data == (uchar *) MAP_FAILED) { + qWarning("mmap /dev/zero failed: %s", strerror(errno)); + close(fd); + return; + } + + mImage = QImage(data, size.width(), size.height(), stride, format); + mBuffer = display->createShmBuffer(fd, size.width(), size.height(), + stride, display->argbVisual()); + close(fd); +} + +QWaylandShmBuffer::~QWaylandShmBuffer(void) +{ + munmap((void *) mImage.constBits(), mImage.byteCount()); + wl_buffer_destroy(mBuffer); +} + +QWaylandShmWindowSurface::QWaylandShmWindowSurface(QWidget *window) + : QWindowSurface(window) + , mBuffer(0) + , mDisplay(QWaylandScreen::waylandScreenFromWidget(window)->display()) +{ +} + +QWaylandShmWindowSurface::~QWaylandShmWindowSurface() +{ +} + +QPaintDevice *QWaylandShmWindowSurface::paintDevice() +{ + return mBuffer->image(); +} + +void QWaylandShmWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(widget); + Q_UNUSED(offset); + QWaylandWindow *ww = (QWaylandWindow *) window()->platformWindow(); + QVector<QRect> rects = region.rects(); + const QRect *r; + int i; + + for (i = 0; i < rects.size(); i++) { + r = &rects.at(i); + wl_surface_damage(ww->surface(), + r->x(), r->y(), r->width(), r->height()); + } +} + +void QWaylandShmWindowSurface::resize(const QSize &size) +{ + QWaylandWindow *ww = (QWaylandWindow *) window()->platformWindow(); + QWindowSurface::resize(size); + QImage::Format format = QApplicationPrivate::platformIntegration()->screens().first()->format(); + + if (mBuffer != NULL && mBuffer->size() == size) + return; + + if (mBuffer != NULL) + delete mBuffer; + + mBuffer = new QWaylandShmBuffer(mDisplay, size, format); + + ww->attach(mBuffer); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/qwaylandshmsurface.h b/src/plugins/platforms/wayland/qwaylandshmsurface.h new file mode 100644 index 0000000..266b290 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandshmsurface.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSURFACE_WAYLAND_H +#define QWINDOWSURFACE_WAYLAND_H + +#include "qwaylandbuffer.h" +#include <QtGui/private/qwindowsurface_p.h> + +#include <QtGui/QPlatformWindow> + +QT_BEGIN_NAMESPACE + +class QWaylandDisplay; + +class QWaylandShmBuffer : public QWaylandBuffer { +public: + QWaylandShmBuffer(QWaylandDisplay *display, + const QSize &size, QImage::Format format); + ~QWaylandShmBuffer(); + QSize size() const { return mImage.size(); } + QImage *image() { return &mImage; } +private: + QImage mImage; +}; + +class QWaylandShmWindowSurface : public QWindowSurface +{ +public: + QWaylandShmWindowSurface(QWidget *window); + ~QWaylandShmWindowSurface(); + + QPaintDevice *paintDevice(); + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + void resize(const QSize &size); + +private: + QWaylandShmBuffer *mBuffer; + QWaylandDisplay *mDisplay; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp new file mode 100644 index 0000000..a28bdfe --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandwindow.h" + +#include "qwaylanddisplay.h" +#include "qwaylandscreen.h" +#include "qwaylandglcontext.h" +#include "qwaylandbuffer.h" + +#include "qwaylanddrmsurface.h" + +#include <QtGui/QWidget> +#include <QtGui/QWindowSystemInterface> + +#include <QDebug> + +QWaylandWindow::QWaylandWindow(QWidget *window) + : QPlatformWindow(window) + , mDisplay(QWaylandScreen::waylandScreenFromWidget(window)->display()) + , mGLContext(0) + , mBuffer(0) +{ + static WId id = 1; + mWindowId = id++; + + mSurface = mDisplay->createSurface(); +} + +QWaylandWindow::~QWaylandWindow() +{ + if (mGLContext) + delete mGLContext; +} + +WId QWaylandWindow::winId() const +{ + return mWindowId; +} + +void QWaylandWindow::setParent(const QPlatformWindow *parent) +{ + QWaylandWindow *wParent = (QWaylandWindow *)parent; + + mParentWindow = wParent; +} + +void QWaylandWindow::setVisible(bool visible) +{ + if (visible) { + wl_surface_set_user_data(mSurface, this); + wl_surface_map_toplevel(mSurface); + } else { + wl_surface_destroy(mSurface); + mSurface = NULL; + } +} + +void QWaylandWindow::attach(QWaylandBuffer *buffer) +{ + if (mSurface) { + wl_surface_attach(mSurface, buffer->buffer(),0,0); + } +} + +void QWaylandWindow::configure(uint32_t time, uint32_t edges, + int32_t x, int32_t y, + int32_t width, int32_t height) +{ + Q_UNUSED(time); + Q_UNUSED(edges); + QRect geometry = QRect(x, y, width, height); + + QWindowSystemInterface::handleGeometryChange(widget(), geometry); +} + +QPlatformGLContext *QWaylandWindow::glContext() const +{ + if (!mGLContext) { + QWaylandWindow *that = const_cast<QWaylandWindow *>(this); + that->mGLContext = new QWaylandGLContext(mDisplay, widget()->platformWindowFormat()); + } + + return mGLContext; +} diff --git a/src/plugins/platforms/wayland/qwaylandwindow.h b/src/plugins/platforms/wayland/qwaylandwindow.h new file mode 100644 index 0000000..8b047d7 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandwindow.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDWINDOW_H +#define QWAYLANDWINDOW_H + +#include <QtGui/QPlatformWindow> + +#include <stdint.h> + +class QWaylandDisplay; +class QWaylandBuffer; + +class QWaylandWindow : public QPlatformWindow +{ +public: + QWaylandWindow(QWidget *window); + ~QWaylandWindow(); + struct wl_surface *surface() { return mSurface; } + + void setVisible(bool visible); + void configure(uint32_t time, uint32_t edges, + int32_t x, int32_t y, int32_t width, int32_t height); + WId winId() const; + void setParent(const QPlatformWindow *parent); + QPlatformGLContext *glContext() const; + void attach(QWaylandBuffer *buffer); + QWaylandBuffer *getBuffer(void) { return mBuffer; } + QWaylandWindow *getParentWindow(void) { return mParentWindow; } + +private: + struct wl_surface *mSurface; + QWaylandDisplay *mDisplay; + QPlatformGLContext *mGLContext; + WId mWindowId; + + QWaylandBuffer *mBuffer; + QWaylandWindow *mParentWindow; +}; + + +#endif // QWAYLANDWINDOW_H diff --git a/src/plugins/platforms/wayland/wayland.pro b/src/plugins/platforms/wayland/wayland.pro new file mode 100644 index 0000000..8ba1408 --- /dev/null +++ b/src/plugins/platforms/wayland/wayland.pro @@ -0,0 +1,39 @@ +TARGET = qwayland +include(../../qpluginbase.pri) + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms + +SOURCES = main.cpp \ + qwaylandintegration.cpp \ + qwaylandshmsurface.cpp \ + qwaylanddrmsurface.cpp \ + qwaylandinputdevice.cpp \ + qwaylandglcontext.cpp \ + qwaylandcursor.cpp \ + qwaylanddisplay.cpp \ + qwaylandwindow.cpp \ + qwaylandscreen.cpp + +HEADERS = qwaylandintegration.h \ + qwaylandcursor.h \ + qwaylanddisplay.h \ + qwaylandwindow.h \ + qwaylandscreen.h \ + qwaylandglcontext.h \ + qwaylandshmsurface.h \ + qwaylanddrmsurface.h \ + qwaylandbuffer.h + +contains(QT_CONFIG, opengl) { + QT += opengl +} +LIBS += -lwayland-client -lxkbcommon -lEGL +unix { + CONFIG += link_pkgconfig + PKGCONFIG += libdrm +} + +include (../fontdatabases/fontconfig/fontconfig.pri) + +target.path += $$[QT_INSTALL_PLUGINS]/platforms +INSTALLS += target diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index 004e3a5..e3291e0 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -1022,6 +1022,7 @@ QScriptEnginePrivate::~QScriptEnginePrivate() while (!ownedAgents.isEmpty()) delete ownedAgents.takeFirst(); + detachAllRegisteredScriptPrograms(); detachAllRegisteredScriptValues(); detachAllRegisteredScriptStrings(); qDeleteAll(m_qobjectData); @@ -1576,6 +1577,14 @@ bool QScriptEnginePrivate::scriptDisconnect(JSC::JSValue signal, JSC::JSValue re #endif +void QScriptEnginePrivate::detachAllRegisteredScriptPrograms() +{ + QSet<QScriptProgramPrivate*>::const_iterator it; + for (it = registeredScriptPrograms.constBegin(); it != registeredScriptPrograms.constEnd(); ++it) + (*it)->detachFromEngine(); + registeredScriptPrograms.clear(); +} + void QScriptEnginePrivate::detachAllRegisteredScriptValues() { QScriptValuePrivate *it; diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h index 05a8901..f8144e9 100644 --- a/src/script/api/qscriptengine_p.h +++ b/src/script/api/qscriptengine_p.h @@ -87,6 +87,7 @@ class QScriptEngineAgent; class QScriptEnginePrivate; class QScriptSyntaxCheckResult; class QScriptEngine; +class QScriptProgramPrivate; namespace QScript { @@ -273,6 +274,10 @@ public: static QScriptSyntaxCheckResult checkSyntax(const QString &program); static bool canEvaluate(const QString &program); + inline void registerScriptProgram(QScriptProgramPrivate *program); + inline void unregisterScriptProgram(QScriptProgramPrivate *program); + void detachAllRegisteredScriptPrograms(); + inline QScriptValuePrivate *allocateScriptValuePrivate(size_t); inline void freeScriptValuePrivate(QScriptValuePrivate *p); @@ -368,6 +373,7 @@ public: static const int maxFreeScriptValues = 256; int freeScriptValuesCount; QScriptStringPrivate *registeredScriptStrings; + QSet<QScriptProgramPrivate*> registeredScriptPrograms; QHash<int, QScriptTypeInfo*> m_typeInfos; int processEventsInterval; QScriptValue abortResult; @@ -566,6 +572,18 @@ inline QByteArray convertToLatin1(const JSC::UString &str) } // namespace QScript +inline void QScriptEnginePrivate::registerScriptProgram(QScriptProgramPrivate *program) +{ + Q_ASSERT(!registeredScriptPrograms.contains(program)); + registeredScriptPrograms.insert(program); +} + +inline void QScriptEnginePrivate::unregisterScriptProgram(QScriptProgramPrivate *program) +{ + Q_ASSERT(registeredScriptPrograms.contains(program)); + registeredScriptPrograms.remove(program); +} + inline QScriptValuePrivate *QScriptEnginePrivate::allocateScriptValuePrivate(size_t size) { if (freeScriptValues) { diff --git a/src/script/api/qscriptprogram.cpp b/src/script/api/qscriptprogram.cpp index da103bb..31af9a0 100644 --- a/src/script/api/qscriptprogram.cpp +++ b/src/script/api/qscriptprogram.cpp @@ -64,6 +64,7 @@ QScriptProgramPrivate::~QScriptProgramPrivate() if (engine) { QScript::APIShim shim(engine); _executable.clear(); + engine->unregisterScriptProgram(this); } } @@ -78,7 +79,10 @@ JSC::EvalExecutable *QScriptProgramPrivate::executable(JSC::ExecState *exec, if (_executable) { if (eng == engine) return _executable.get(); - _executable = 0; + // "Migrating" to another engine; clean up old state + QScript::APIShim shim(engine); + _executable.clear(); + engine->unregisterScriptProgram(this); } WTF::PassRefPtr<QScript::UStringSourceProviderWithFeedback> provider = QScript::UStringSourceProviderWithFeedback::create(sourceCode, fileName, firstLineNumber, eng); @@ -86,10 +90,19 @@ JSC::EvalExecutable *QScriptProgramPrivate::executable(JSC::ExecState *exec, JSC::SourceCode source(provider, firstLineNumber); //after construction of SourceCode provider variable will be null. _executable = JSC::EvalExecutable::create(exec, source); engine = eng; + engine->registerScriptProgram(this); isCompiled = false; return _executable.get(); } +void QScriptProgramPrivate::detachFromEngine() +{ + _executable.clear(); + sourceId = -1; + isCompiled = false; + engine = 0; +} + /*! Constructs a null QScriptProgram. */ @@ -122,9 +135,6 @@ QScriptProgram::QScriptProgram(const QScriptProgram &other) */ QScriptProgram::~QScriptProgram() { - // Q_D(QScriptProgram); - // if (d->engine && (d->ref == 1)) - // d->engine->unregisterScriptProgram(d); } /*! @@ -132,9 +142,6 @@ QScriptProgram::~QScriptProgram() */ QScriptProgram &QScriptProgram::operator=(const QScriptProgram &other) { - // if (d_func() && d_func()->engine && (d_func()->ref == 1)) - // d_func()->engine->unregisterScriptProgram(d_func()); - // } d_ptr = other.d_ptr; return *this; } diff --git a/src/script/api/qscriptprogram_p.h b/src/script/api/qscriptprogram_p.h index d2fd234..e7809ab 100644 --- a/src/script/api/qscriptprogram_p.h +++ b/src/script/api/qscriptprogram_p.h @@ -61,6 +61,7 @@ public: JSC::EvalExecutable *executable(JSC::ExecState *exec, QScriptEnginePrivate *engine); + void detachFromEngine(); QBasicAtomicInt ref; diff --git a/src/script/bridge/qscriptclassobject.cpp b/src/script/bridge/qscriptclassobject.cpp index 9285883..2085756 100644 --- a/src/script/bridge/qscriptclassobject.cpp +++ b/src/script/bridge/qscriptclassobject.cpp @@ -183,6 +183,10 @@ void ClassObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecSt JSC::PropertyNameArray &propertyNames, JSC::EnumerationMode mode) { + // For compatibility with the old back-end, normal JS properties + // are added first. + QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, mode); + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); QScript::SaveFrameHelper saveFrame(engine, exec); QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); @@ -195,7 +199,6 @@ void ClassObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecSt } delete it; } - QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, mode); } JSC::CallType ClassObjectDelegate::getCallData(QScriptObject*, JSC::CallData &callData) diff --git a/src/script/script.pro b/src/script/script.pro index 34f4a88..162eb9c 100644 --- a/src/script/script.pro +++ b/src/script/script.pro @@ -89,7 +89,6 @@ include(script.pri) symbian { TARGET.UID3=0x2001B2E1 - LIBS += -lhal } symbian { diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp index 1573d02..0b534d9 100644 --- a/src/sql/drivers/odbc/qsql_odbc.cpp +++ b/src/sql/drivers/odbc/qsql_odbc.cpp @@ -214,7 +214,7 @@ static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode state_, &nativeCode_, 0, - NULL, + 0, &msgLen); if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && msgLen > 0) description_.resize(msgLen+1); diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp index 5a32d1a..34277b3 100644 --- a/src/sql/drivers/psql/qsql_psql.cpp +++ b/src/sql/drivers/psql/qsql_psql.cpp @@ -1021,6 +1021,9 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from " "pg_namespace where pg_namespace.nspname = '%1') AND ").arg(schema)); break; + case QPSQLDriver::VersionUnknown: + qFatal("PSQL version is unknown"); + break; } i.exec(stmt.arg(tbl)); @@ -1110,6 +1113,9 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from " "pg_namespace where pg_namespace.nspname = '%1')").arg(schema)); break; + case QPSQLDriver::VersionUnknown: + qFatal("PSQL version is unknown"); + break; } QSqlQuery query(createResult()); diff --git a/src/testlib/qtestmouse.h b/src/testlib/qtestmouse.h index 35752b0..5abb019 100644 --- a/src/testlib/qtestmouse.h +++ b/src/testlib/qtestmouse.h @@ -107,7 +107,11 @@ namespace QTest break; case MouseMove: QCursor::setPos(widget->mapToGlobal(pos)); +#ifdef QT_MAC_USE_COCOA + QTest::qWait(20); +#else qApp->processEvents(); +#endif return; default: QTEST_ASSERT(false); |