diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/qmake/generators/unix/unixmake2.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/qmake/generators/unix/unixmake2.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/qmake/generators/unix/unixmake2.cpp 2014-06-19 12:08:02.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/qmake/generators/unix/unixmake2.cpp 2014-08-13 04:36:48.326242431 +0200 @@ -117,7 +117,7 @@ << varGlue("DEFINES","-D"," -D","") << endl; t << "CFLAGS = " << var("QMAKE_CFLAGS") << " $(DEFINES)\n"; t << "CXXFLAGS = " << var("QMAKE_CXXFLAGS") << " $(DEFINES)\n"; - t << "INCPATH = -I" << specdir(); + t << "INCPATH = -I/usr/include -I" << specdir(); if(!project->isActiveConfig("no_include_pwd")) { QString pwd = escapeFilePath(fileFixify(qmake_getpwd())); if(pwd.isEmpty()) @@ -1344,7 +1344,7 @@ QTextStream t(&ft); t << "# " << lname << " - a libtool library file\n"; t << "# Generated by qmake/libtool (" QMAKE_VERSION_STR ") (Qt " - << QT_VERSION_STR << ") on: " << QDateTime::currentDateTime().toString(); + << QT_VERSION_STR << ")"; t << "\n"; t << "# The name that we can dlopen(3).\n" diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/qmake/generators/unix/unixmake2.cpp.orig qt-everywhere-opensource-src-5.3.1.new/qtbase/qmake/generators/unix/unixmake2.cpp.orig --- qt-everywhere-opensource-src-5.3.1/qtbase/qmake/generators/unix/unixmake2.cpp.orig 1970-01-01 01:00:00.000000000 +0100 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/qmake/generators/unix/unixmake2.cpp.orig 2014-08-13 04:36:48.323242404 +0200 @@ -0,0 +1,1403 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the qmake application of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "unixmake.h" +#include "option.h" +#include "meta.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +UnixMakefileGenerator::UnixMakefileGenerator() : MakefileGenerator(), init_flag(false), include_deps(false) +{ + +} + +void +UnixMakefileGenerator::writePrlFile(QTextStream &t) +{ + MakefileGenerator::writePrlFile(t); + // libtool support + + if(project->isActiveConfig("create_libtool") && project->first("TEMPLATE") == "lib") { //write .la + if(project->isActiveConfig("compile_libtool")) + warn_msg(WarnLogic, "create_libtool specified with compile_libtool can lead to conflicting .la\n" + "formats, create_libtool has been disabled\n"); + else + writeLibtoolFile(); + } + // pkg-config support + if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib") + writePkgConfigFile(); +} + +bool +UnixMakefileGenerator::writeMakefile(QTextStream &t) +{ + + writeHeader(t); + if (writeDummyMakefile(t)) + return true; + + if (project->values("TEMPLATE").first() == "app" || + project->values("TEMPLATE").first() == "lib" || + project->values("TEMPLATE").first() == "aux") { + if(Option::mkfile::do_stub_makefile && MakefileGenerator::writeStubMakefile(t)) + return true; + writeMakeParts(t); + return MakefileGenerator::writeMakefile(t); + } else if(project->values("TEMPLATE").first() == "subdirs") { + MakefileGenerator::writeSubDirs(t); + return true; + } + return false; +} + +void +UnixMakefileGenerator::writeMakeParts(QTextStream &t) +{ + QString deps = fileFixify(Option::output.fileName()), target_deps, prl; + bool do_incremental = (project->isActiveConfig("incremental") && + !project->values("QMAKE_INCREMENTAL").isEmpty() && + (!project->values("QMAKE_APP_FLAG").isEmpty() || + (!project->isActiveConfig("staticlib")))), + src_incremental=false; + + ProStringList &bundledFiles = project->values("QMAKE_BUNDLED_FILES"); + + t << "####### Compiler, tools and options\n\n"; + t << "CC = " << var("QMAKE_CC") << endl; + t << "CXX = " << var("QMAKE_CXX") << endl; + t << "DEFINES = " + << varGlue("PRL_EXPORT_DEFINES","-D"," -D"," ") + << varGlue("DEFINES","-D"," -D","") << endl; + t << "CFLAGS = " << var("QMAKE_CFLAGS") << " $(DEFINES)\n"; + t << "CXXFLAGS = " << var("QMAKE_CXXFLAGS") << " $(DEFINES)\n"; + t << "INCPATH = -I" << specdir(); + if(!project->isActiveConfig("no_include_pwd")) { + QString pwd = escapeFilePath(fileFixify(qmake_getpwd())); + if(pwd.isEmpty()) + pwd = "."; + t << " -I" << pwd; + } + { + QString isystem = var("QMAKE_CFLAGS_ISYSTEM"); + const ProStringList &incs = project->values("INCLUDEPATH"); + for(int i = 0; i < incs.size(); ++i) { + ProString inc = escapeFilePath(incs.at(i)); + if (inc.isEmpty()) + continue; + + if (!isystem.isEmpty() && isSystemInclude(inc.toQString())) + t << ' ' << isystem << ' ' << inc; + else + t << " -I" << inc; + } + } + if(!project->isEmpty("QMAKE_FRAMEWORKPATH_FLAGS")) + t << " " << var("QMAKE_FRAMEWORKPATH_FLAGS"); + t << endl; + + if(!project->isActiveConfig("staticlib")) { + t << "LINK = " << var("QMAKE_LINK") << endl; + t << "LFLAGS = " << var("QMAKE_LFLAGS") << endl; + t << "LIBS = $(SUBLIBS) " << var("QMAKE_LIBS") << " " << var("QMAKE_LIBS_PRIVATE") << endl; + } + + t << "AR = " << var("QMAKE_AR") << endl; + t << "RANLIB = " << var("QMAKE_RANLIB") << endl; + t << "QMAKE = " << var("QMAKE_QMAKE") << endl; + t << "TAR = " << var("QMAKE_TAR") << endl; + t << "COMPRESS = " << var("QMAKE_GZIP") << endl; + if(project->isActiveConfig("compile_libtool")) + t << "LIBTOOL = " << var("QMAKE_LIBTOOL") << endl; + t << "COPY = " << var("QMAKE_COPY") << endl; + t << "SED = " << var("QMAKE_STREAM_EDITOR") << endl; + t << "COPY_FILE = " << var("QMAKE_COPY_FILE") << endl; + t << "COPY_DIR = " << var("QMAKE_COPY_DIR") << endl; + t << "STRIP = " << var("QMAKE_STRIP") << endl; + t << "INSTALL_FILE = " << var("QMAKE_INSTALL_FILE") << endl; + t << "INSTALL_DIR = " << var("QMAKE_INSTALL_DIR") << endl; + t << "INSTALL_PROGRAM = " << var("QMAKE_INSTALL_PROGRAM") << endl; + + t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; + t << "SYMLINK = " << var("QMAKE_SYMBOLIC_LINK") << endl; + t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl; + t << "MOVE = " << var("QMAKE_MOVE") << endl; + t << "CHK_DIR_EXISTS= " << var("QMAKE_CHK_DIR_EXISTS") << endl; + t << "MKDIR = " << var("QMAKE_MKDIR") << endl; + + t << endl; + + t << "####### Output directory\n\n"; + if (! project->values("OBJECTS_DIR").isEmpty()) + t << "OBJECTS_DIR = " << var("OBJECTS_DIR") << endl; + else + t << "OBJECTS_DIR = ./\n"; + t << endl; + + /* files */ + t << "####### Files\n\n"; + t << "SOURCES = " << valList(escapeFilePaths(project->values("SOURCES"))) << " " + << valList(escapeFilePaths(project->values("GENERATED_SOURCES"))) << endl; + if(do_incremental) { + const ProStringList &objs = project->values("OBJECTS"); + const ProStringList &incrs = project->values("QMAKE_INCREMENTAL"); + ProStringList incrs_out; + t << "OBJECTS = "; + for (ProStringList::ConstIterator objit = objs.begin(); objit != objs.end(); ++objit) { + bool increment = false; + for (ProStringList::ConstIterator incrit = incrs.begin(); incrit != incrs.end(); ++incrit) { + if ((*objit).toQString().indexOf(QRegExp((*incrit).toQString(), Qt::CaseSensitive, + QRegExp::Wildcard)) != -1) { + increment = true; + incrs_out.append((*objit)); + break; + } + } + if(!increment) + t << "\\\n\t\t" << (*objit); + } + if(incrs_out.count() == objs.count()) { //we just switched places, no real incrementals to be done! + t << escapeFilePaths(incrs_out).join(" \\\n\t\t") << endl; + } else if(!incrs_out.count()) { + t << endl; + } else { + src_incremental = true; + t << endl; + t << "INCREMENTAL_OBJECTS = " << escapeFilePaths(incrs_out).join(" \\\n\t\t") << endl; + } + } else { + t << "OBJECTS = " << valList(escapeFilePaths(project->values("OBJECTS"))) << endl; + } + if(do_incremental && !src_incremental) + do_incremental = false; + t << "DIST = " << valList(fileFixify(project->values("DISTFILES").toQStringList())) << " " + << valList(escapeFilePaths(project->values("SOURCES"))) << endl; + t << "QMAKE_TARGET = " << var("QMAKE_ORIG_TARGET") << endl; + // The comment is important for mingw32-make.exe on Windows as otherwise trailing slashes + // would be interpreted as line continuation. The lack of spacing between the value and the + // comment is also important as otherwise quoted use of "$(DESTDIR)" would include this + // spacing. + t << "DESTDIR = " << var("DESTDIR") << "#avoid trailing-slash linebreak\n"; + if(project->isActiveConfig("compile_libtool")) + t << "TARGETL = " << var("TARGET_la") << endl; + t << "TARGET = " << escapeFilePath(var("TARGET")) << endl; + if(project->isActiveConfig("plugin")) { + t << "TARGETD = " << escapeFilePath(var("TARGET")) << endl; + } else if(!project->isActiveConfig("staticlib") && project->values("QMAKE_APP_FLAG").isEmpty()) { + t << "TARGETA = " << escapeFilePath(var("TARGETA")) << endl; + if(!project->isEmpty("QMAKE_BUNDLE")) { + t << "TARGETD = " << escapeFilePath(var("TARGET_x.y")) << endl; + t << "TARGET0 = " << escapeFilePath(var("TARGET_")) << endl; + } else if (!project->isActiveConfig("unversioned_libname")) { + t << "TARGET0 = " << escapeFilePath(var("TARGET_")) << endl; + if (project->isEmpty("QMAKE_HPUX_SHLIB")) { + t << "TARGETD = " << escapeFilePath(var("TARGET_x.y.z")) << endl; + t << "TARGET1 = " << escapeFilePath(var("TARGET_x")) << endl; + t << "TARGET2 = " << escapeFilePath(var("TARGET_x.y")) << endl; + } else { + t << "TARGETD = " << escapeFilePath(var("TARGET_x")) << endl; + } + } + } + writeExtraCompilerVariables(t); + writeExtraVariables(t); + t << endl; + + // blasted includes + const ProStringList &qeui = project->values("QMAKE_EXTRA_INCLUDES"); + ProStringList::ConstIterator it; + for(it = qeui.begin(); it != qeui.end(); ++it) + t << "include " << (*it) << endl; + + /* rules */ + t << "first: all\n"; + t << "####### Implicit rules\n\n"; + t << ".SUFFIXES: " << Option::obj_ext; + for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) + t << " " << (*cit); + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) + t << " " << (*cppit); + t << endl << endl; + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); cppit != Option::cpp_ext.end(); ++cppit) + t << (*cppit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CXX_IMP") << endl << endl; + for(QStringList::Iterator cit = Option::c_ext.begin(); cit != Option::c_ext.end(); ++cit) + t << (*cit) << Option::obj_ext << ":\n\t" << var("QMAKE_RUN_CC_IMP") << endl << endl; + + if(include_deps) { + if (project->isActiveConfig("gcc_MD_depends")) { + ProStringList objects = project->values("OBJECTS"); + for (ProStringList::Iterator it = objects.begin(); it != objects.end(); ++it) { + QString d_file = (*it).toQString().replace(QRegExp(Option::obj_ext + "$"), ".d"); + t << "-include " << d_file << endl; + project->values("QMAKE_DISTCLEAN") << d_file; + } + } else { + QString cmd=var("QMAKE_CFLAGS_DEPS") + " "; + cmd += varGlue("DEFINES","-D"," -D","") + varGlue("PRL_EXPORT_DEFINES"," -D"," -D",""); + if(!project->isEmpty("QMAKE_ABSOLUTE_SOURCE_PATH")) + cmd += " -I" + project->first("QMAKE_ABSOLUTE_SOURCE_PATH") + " "; + cmd += " $(INCPATH) " + varGlue("DEPENDPATH", "-I", " -I", ""); + ProString odir; + if(!project->values("OBJECTS_DIR").isEmpty()) + odir = project->first("OBJECTS_DIR"); + + QString pwd = escapeFilePath(fileFixify(qmake_getpwd())); + + t << "###### Dependencies\n\n"; + t << odir << ".deps/%.d: " << pwd << "/%.cpp\n\t"; + if(project->isActiveConfig("echo_depend_creation")) + t << "@echo Creating depend for $<\n\t"; + t << mkdir_p_asstring("$(@D)", false) << "\n\t" + << "@$(CXX) " << cmd << " $< | sed \"s,^\\($(*F).o\\):," << odir << "\\1:,g\" >$@\n\n"; + + t << odir << ".deps/%.d: " << pwd << "/%.c\n\t"; + if(project->isActiveConfig("echo_depend_creation")) + t << "@echo Creating depend for $<\n\t"; + t << mkdir_p_asstring("$(@D)", false) << "\n\t" + << "@$(CC) " << cmd << " $< | sed \"s,^\\($(*F).o\\):," << odir << "\\1:,g\" >$@\n\n"; + + static const char * const src[] = { "SOURCES", "GENERATED_SOURCES", 0 }; + for (int x = 0; src[x]; x++) { + const ProStringList &l = project->values(src[x]); + for (ProStringList::ConstIterator it = l.begin(); it != l.end(); ++it) { + if(!(*it).isEmpty()) { + QString d_file; + for(QStringList::Iterator cit = Option::c_ext.begin(); + cit != Option::c_ext.end(); ++cit) { + if((*it).endsWith((*cit))) { + d_file = (*it).left((*it).length() - (*cit).length()).toQString(); + break; + } + } + if(d_file.isEmpty()) { + for(QStringList::Iterator cppit = Option::cpp_ext.begin(); + cppit != Option::cpp_ext.end(); ++cppit) { + if((*it).endsWith((*cppit))) { + d_file = (*it).left((*it).length() - (*cppit).length()).toQString(); + break; + } + } + } + + if(!d_file.isEmpty()) { + d_file = odir + ".deps/" + fileFixify(d_file, pwd, Option::output_dir) + ".d"; + QStringList deps = findDependencies((*it).toQString()).filter(QRegExp( + "((^|/)" + Option::h_moc_mod + "|" + Option::cpp_moc_ext + "$)")); + if(!deps.isEmpty()) + t << d_file << ": " << deps.join(' ') << endl; + t << "-include " << d_file << endl; + project->values("QMAKE_DISTCLEAN") += d_file; + } + } + } + } + } + } + + t << "####### Build rules\n\n"; + if(!project->values("SUBLIBS").isEmpty()) { + ProString libdir = "tmp/"; + if(!project->isEmpty("SUBLIBS_DIR")) + libdir = project->first("SUBLIBS_DIR"); + t << "SUBLIBS = "; + const ProStringList &l = project->values("SUBLIBS"); + for (ProStringList::ConstIterator it = l.begin(); it != l.end(); ++it) + t << libdir << project->first("QMAKE_PREFIX_STATICLIB") << (*it) << "." + << project->first("QMAKE_EXTENSION_STATICLIB") << " "; + t << endl << endl; + } + if ((project->isActiveConfig("depend_prl") || project->isActiveConfig("fast_depend_prl")) + && !project->isEmpty("QMAKE_PRL_INTERNAL_FILES")) { + const ProStringList &l = project->values("QMAKE_PRL_INTERNAL_FILES"); + ProStringList::ConstIterator it; + for(it = l.begin(); it != l.end(); ++it) { + QMakeMetaInfo libinfo(project); + if (libinfo.readLib((*it).toQString()) && !libinfo.isEmpty("QMAKE_PRL_BUILD_DIR")) { + ProString dir; + int slsh = (*it).lastIndexOf(Option::dir_sep); + if(slsh != -1) + dir = (*it).left(slsh + 1); + QString targ = dir + libinfo.first("QMAKE_PRL_TARGET"); + target_deps += " " + targ; + t << targ; + if (project->isActiveConfig("fast_depend_prl")) + t << ":\n\t@echo \"Creating '"; + else + t << ": FORCE\n\t@echo \"Creating/updating '"; + t << targ << "'\"\n\t" + << "(cd " << libinfo.first("QMAKE_PRL_BUILD_DIR") << ";" + << "$(MAKE))\n"; + } + } + } + if (!project->values("QMAKE_APP_FLAG").isEmpty() || project->first("TEMPLATE") == "aux") { + QString destdir = project->first("DESTDIR").toQString(); + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION").toQString(); + if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/")) + bundle_loc.prepend("/"); + if(!bundle_loc.endsWith("/")) + bundle_loc += "/"; + destdir += project->first("QMAKE_BUNDLE") + bundle_loc; + } + if(do_incremental) { + //incremental target + QString incr_target = var("TARGET") + "_incremental"; + if(incr_target.indexOf(Option::dir_sep) != -1) + incr_target = incr_target.right(incr_target.length() - + (incr_target.lastIndexOf(Option::dir_sep) + 1)); + QString incr_deps, incr_objs; + if(project->first("QMAKE_INCREMENTAL_STYLE") == "ld") { + QString incr_target_dir = var("OBJECTS_DIR") + incr_target + Option::obj_ext; + //actual target + t << incr_target_dir << ": $(OBJECTS)\n\t" + << "ld -r -o "<< incr_target_dir << " $(OBJECTS)\n"; + //communicated below + deps.prepend(incr_target_dir + " "); + incr_deps = "$(INCREMENTAL_OBJECTS)"; + if(!incr_objs.isEmpty()) + incr_objs += " "; + incr_objs += incr_target_dir; + } else { + //actual target + QString incr_target_dir = var("DESTDIR") + "lib" + incr_target + "." + + project->values("QMAKE_EXTENSION_SHLIB").first(); + QString incr_lflags = var("QMAKE_LFLAGS_SHLIB") + " "; + if(project->isActiveConfig("debug")) + incr_lflags += var("QMAKE_LFLAGS_DEBUG"); + else if (project->isActiveConfig("debug_info")) + incr_lflags += var("QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO"); + else + incr_lflags += var("QMAKE_LFLAGS_RELEASE"); + t << incr_target_dir << ": $(INCREMENTAL_OBJECTS)\n\t"; + if(!destdir.isEmpty()) + t << "\n\t" << mkdir_p_asstring(destdir) << "\n\t"; + t << "$(LINK) " << incr_lflags << " -o "<< incr_target_dir << + " $(INCREMENTAL_OBJECTS)\n"; + //communicated below + if(!destdir.isEmpty()) { + if(!incr_objs.isEmpty()) + incr_objs += " "; + incr_objs += "-L" + destdir; + } else { + if(!incr_objs.isEmpty()) + incr_objs += " "; + incr_objs += "-L" + qmake_getpwd(); + } + if(!incr_objs.isEmpty()) + incr_objs += " "; + incr_objs += " -l" + incr_target; + deps.prepend(incr_target_dir + " "); + incr_deps = "$(OBJECTS)"; + } + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << "$(TARGET)" + << endl << endl; + + //real target + t << var("TARGET") << ": " << var("PRE_TARGETDEPS") << " " << incr_deps << " " << target_deps + << " " << var("POST_TARGETDEPS") << "\n\t"; + if(!destdir.isEmpty()) + t << "\n\t" << mkdir_p_asstring(destdir) << "\n\t"; + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << var("QMAKE_PRE_LINK") << "\n\t"; + t << "$(LINK) $(LFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(TARGET) " << incr_deps << " " << incr_objs << " $(OBJCOMP) $(LIBS)"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else { + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << "$(TARGET)" + << endl << endl; + + t << "$(TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " + << target_deps << " " << var("POST_TARGETDEPS") << "\n\t"; + if (project->first("TEMPLATE") != "aux") { + if (!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + if (!project->isEmpty("QMAKE_PRE_LINK")) + t << var("QMAKE_PRE_LINK") << "\n\t"; + t << "$(LINK) $(LFLAGS) " << var("QMAKE_LINK_O_FLAG") << "$(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)"; + if (!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + } + t << endl << endl; + } + } else if(!project->isActiveConfig("staticlib")) { + QString destdir = unescapeFilePath(project->first("DESTDIR").toQString()), incr_deps; + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION").toQString(); + if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/")) + bundle_loc.prepend("/"); + if(!bundle_loc.endsWith("/")) + bundle_loc += "/"; + destdir += project->first("QMAKE_BUNDLE") + bundle_loc; + } + destdir = escapeFilePath(destdir); + + if(do_incremental) { + ProString s_ext = project->first("QMAKE_EXTENSION_SHLIB"); + QString incr_target = var("QMAKE_ORIG_TARGET").replace( + QRegExp("\\." + s_ext), "").replace(QRegExp("^lib"), "") + "_incremental"; + if(incr_target.indexOf(Option::dir_sep) != -1) + incr_target = incr_target.right(incr_target.length() - + (incr_target.lastIndexOf(Option::dir_sep) + 1)); + incr_target = escapeFilePath(incr_target); + + if(project->first("QMAKE_INCREMENTAL_STYLE") == "ld") { + QString incr_target_dir = escapeFilePath(var("OBJECTS_DIR") + incr_target + Option::obj_ext); + //actual target + const QString link_deps = "$(OBJECTS) "; + t << incr_target_dir << ": " << link_deps << "\n\t" + << "ld -r -o " << incr_target_dir << " " << link_deps << endl; + //communicated below + ProStringList &cmd = project->values("QMAKE_LINK_SHLIB_CMD"); + cmd[0] = cmd.at(0).toQString().replace("$(OBJECTS) ", "$(INCREMENTAL_OBJECTS)"); //ick + cmd.append(incr_target_dir); + deps.prepend(incr_target_dir + " "); + incr_deps = "$(INCREMENTAL_OBJECTS)"; + } else { + //actual target + QString incr_target_dir = escapeFilePath(destdir + "lib" + incr_target + "." + s_ext); + QString incr_lflags = var("QMAKE_LFLAGS_SHLIB") + " "; + if(!project->isEmpty("QMAKE_LFLAGS_INCREMENTAL")) + incr_lflags += var("QMAKE_LFLAGS_INCREMENTAL") + " "; + if(project->isActiveConfig("debug")) + incr_lflags += var("QMAKE_LFLAGS_DEBUG"); + else if (project->isActiveConfig("debug_info")) + incr_lflags += var("QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO"); + else + incr_lflags += var("QMAKE_LFLAGS_RELEASE"); + t << incr_target_dir << ": $(INCREMENTAL_OBJECTS)\n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir, false) << "\n\t"; + t << "$(LINK) " << incr_lflags << " " << var("QMAKE_LINK_O_FLAG") << incr_target_dir << + " $(INCREMENTAL_OBJECTS)\n"; + //communicated below + ProStringList &cmd = project->values("QMAKE_LINK_SHLIB_CMD"); + if(!destdir.isEmpty()) + cmd.append(" -L" + destdir); + cmd.append(" -l" + incr_target); + deps.prepend(incr_target_dir + " "); + incr_deps = "$(OBJECTS)"; + } + + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") + << " " << destdir << "$(TARGET)\n\n"; + + //real target + t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") << " " + << incr_deps << " $(SUBLIBS) " << target_deps << " " << var("POST_TARGETDEPS"); + } else { + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << " " << + destdir << "$(TARGET)\n\n"; + t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") + << " $(OBJECTS) $(SUBLIBS) $(OBJCOMP) " << target_deps + << " " << var("POST_TARGETDEPS"); + } + if(!destdir.isEmpty()) + t << "\n\t" << mkdir_p_asstring(destdir, false); + if(!project->isEmpty("QMAKE_PRE_LINK")) + t << "\n\t" << var("QMAKE_PRE_LINK"); + + if(project->isActiveConfig("compile_libtool")) { + t << "\n\t" + << var("QMAKE_LINK_SHLIB_CMD"); + } else if(project->isActiveConfig("plugin")) { + t << "\n\t" + << "-$(DEL_FILE) $(TARGET)\n\t" + << var("QMAKE_LINK_SHLIB_CMD"); + if(!destdir.isEmpty()) + t << "\n\t" + << "-$(MOVE) $(TARGET) " << destdir << " "; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else if(!project->isEmpty("QMAKE_BUNDLE")) { + QString currentLink = destdir + "Versions/Current"; + bundledFiles << currentLink << destdir + "$(TARGET)"; + t << "\n\t" + << "-$(DEL_FILE) $(TARGET) $(TARGET0) $(DESTDIR)$(TARGET0)\n\t" + << var("QMAKE_LINK_SHLIB_CMD") << "\n\t" + << mkdir_p_asstring("\"`dirname $(DESTDIR)$(TARGETD)`\"", false) << "\n\t" + << "-$(MOVE) $(TARGET) $(DESTDIR)$(TARGETD)\n\t" + << mkdir_p_asstring("\"`dirname $(DESTDIR)$(TARGET0)`\"", false) << "\n\t" + << varGlue("QMAKE_LN_SHLIB", "-", " ", + " Versions/Current/$(TARGET) $(DESTDIR)$(TARGET0)") << "\n\t" + << "-$(DEL_FILE) " << currentLink << "\n\t" + << varGlue("QMAKE_LN_SHLIB","-"," ", " " + project->first("QMAKE_FRAMEWORK_VERSION") + + " " + currentLink) << "\n\t"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else if(project->isEmpty("QMAKE_HPUX_SHLIB")) { + t << "\n\t"; + + if (!project->isActiveConfig("unversioned_libname")) + t << "-$(DEL_FILE) $(TARGET) $(TARGET0) $(TARGET1) $(TARGET2)"; + else + t << "-$(DEL_FILE) $(TARGET)"; + + t << "\n\t" << var("QMAKE_LINK_SHLIB_CMD"); + + if (!project->isActiveConfig("unversioned_libname")) { + t << "\n\t" + << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET0)") << "\n\t" + << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET1)") << "\n\t" + << varGlue("QMAKE_LN_SHLIB","-"," "," $(TARGET) $(TARGET2)"); + } + if (!destdir.isEmpty()) { + t << "\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET)\n\t" + << "-$(MOVE) $(TARGET) " << destdir << " "; + + if (!project->isActiveConfig("unversioned_libname")) { + t << "\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET0)\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET1)\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET2)\n\t" + << "-$(MOVE) $(TARGET0) " << destdir << " \n\t" + << "-$(MOVE) $(TARGET1) " << destdir << " \n\t" + << "-$(MOVE) $(TARGET2) " << destdir << " "; + } + } + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } else { + t << "\n\t" + << "-$(DEL_FILE) $(TARGET) $(TARGET0)\n\t" + << var("QMAKE_LINK_SHLIB_CMD") << "\n\t"; + t << varGlue("QMAKE_LN_SHLIB",""," "," $(TARGET) $(TARGET0)"); + if(!destdir.isEmpty()) + t << "\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET)\n\t" + << "-$(DEL_FILE) " << destdir << "$(TARGET0)\n\t" + << "-$(MOVE) $(TARGET) " << destdir << " \n\t" + << "-$(MOVE) $(TARGET0) " << destdir << " \n\t"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\n\t" << var("QMAKE_POST_LINK"); + t << endl << endl; + } + t << endl << endl; + + if (! project->isActiveConfig("plugin")) { + t << "staticlib: $(TARGETA)\n\n"; + t << "$(TARGETA): " << var("PRE_TARGETDEPS") << " $(OBJECTS) $(OBJCOMP)"; + if(do_incremental) + t << " $(INCREMENTAL_OBJECTS)"; + t << " " << var("POST_TARGETDEPS") << "\n\t" + << "-$(DEL_FILE) $(TARGETA) \n\t" + << var("QMAKE_AR_CMD"); + if(do_incremental) + t << " $(INCREMENTAL_OBJECTS)"; + if(!project->isEmpty("QMAKE_RANLIB")) + t << "\n\t$(RANLIB) $(TARGETA)"; + t << endl << endl; + } + } else { + QString destdir = project->first("DESTDIR").toQString(); + t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << destdir << "$(TARGET) " + << varGlue("QMAKE_AR_SUBLIBS", destdir, " " + destdir, "") << "\n\n" + << "staticlib: " << destdir << "$(TARGET)\n\n"; + if(project->isEmpty("QMAKE_AR_SUBLIBS")) { + t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") + << " $(OBJECTS) $(OBJCOMP) " << var("POST_TARGETDEPS") << "\n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "-$(DEL_FILE) $(TARGET)\n\t" + << var("QMAKE_AR_CMD") << "\n"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\t" << var("QMAKE_POST_LINK") << "\n"; + if(!project->isEmpty("QMAKE_RANLIB")) + t << "\t$(RANLIB) $(TARGET)\n"; + if(!destdir.isEmpty()) + t << "\t-$(DEL_FILE) " << destdir << "$(TARGET)\n" + << "\t-$(MOVE) $(TARGET) " << destdir << " \n"; + } else { + int max_files = project->first("QMAKE_MAX_FILES_PER_AR").toInt(); + ProStringList objs = project->values("OBJECTS") + project->values("OBJCOMP"), + libs = project->values("QMAKE_AR_SUBLIBS"); + libs.prepend("$(TARGET)"); + for (ProStringList::Iterator libit = libs.begin(), objit = objs.begin(); + libit != libs.end(); ++libit) { + ProStringList build; + for(int cnt = 0; cnt < max_files && objit != objs.end(); ++objit, cnt++) + build << (*objit); + QString ar; + if((*libit) == "$(TARGET)") { + t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") + << " " << var("POST_TARGETDEPS") << valList(build) << "\n\t"; + ar = project->first("QMAKE_AR_CMD").toQString(); + ar = ar.replace("$(OBJECTS)", build.join(' ')); + } else { + t << (*libit) << ": " << valList(build) << "\n\t"; + ar = "$(AR) " + (*libit) + " " + build.join(' '); + } + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "-$(DEL_FILE) " << (*libit) << "\n\t" + << ar << "\n"; + if(!project->isEmpty("QMAKE_POST_LINK")) + t << "\t" << var("QMAKE_POST_LINK") << "\n"; + if(!project->isEmpty("QMAKE_RANLIB")) + t << "\t$(RANLIB) " << (*libit) << "\n"; + if(!destdir.isEmpty()) + t << "\t-$(DEL_FILE) " << destdir << (*libit) << "\n" + << "\t-$(MOVE) " << (*libit) << " " << destdir << " \n"; + } + } + t << endl << endl; + } + + writeMakeQmake(t); + if(project->isEmpty("QMAKE_FAILED_REQUIREMENTS") && !project->isActiveConfig("no_autoqmake")) { + QStringList meta_files; + if(project->isActiveConfig("create_libtool") && project->first("TEMPLATE") == "lib" && + !project->isActiveConfig("compile_libtool")) { //libtool + meta_files += libtoolFileName(); + } + if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib") { //pkg-config + meta_files += pkgConfigFileName(); + } + if(!meta_files.isEmpty()) + t << escapeDependencyPaths(meta_files).join(" ") << ": \n\t" + << "@$(QMAKE) -prl " << buildArgs() << " " << project->projectFile() << endl; + } + + if(!project->first("QMAKE_PKGINFO").isEmpty()) { + ProString pkginfo = escapeFilePath(project->first("QMAKE_PKGINFO")); + QString destdir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents"; + t << pkginfo << ": \n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "@$(DEL_FILE) " << pkginfo << "\n\t" + << "@echo \"APPL" + << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) + << "\" >" << pkginfo << endl; + } + if(!project->first("QMAKE_BUNDLE_RESOURCE_FILE").isEmpty()) { + ProString resources = escapeFilePath(project->first("QMAKE_BUNDLE_RESOURCE_FILE")); + bundledFiles << resources; + QString destdir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources"; + t << resources << ": \n\t"; + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "@touch " << resources << "\n\t\n"; + } + if(!project->isEmpty("QMAKE_BUNDLE")) { + //copy the plist + QString info_plist = escapeFilePath(fileFixify(project->first("QMAKE_INFO_PLIST").toQString())), + info_plist_out = escapeFilePath(project->first("QMAKE_INFO_PLIST_OUT").toQString()); + if (info_plist.isEmpty()) + info_plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"); + bundledFiles << info_plist_out; + QString destdir = info_plist_out.section(Option::dir_sep, 0, -2); + t << info_plist_out << ": \n\t"; + if(!destdir.isEmpty()) + t << mkdir_p_asstring(destdir, false) << "\n\t"; + ProStringList commonSedArgs; + if (!project->values("VERSION").isEmpty()) + commonSedArgs << "-e \"s,@SHORT_VERSION@," << project->first("VER_MAJ") << "." << project->first("VER_MIN") << ",g\" "; + commonSedArgs << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" "; + if(project->first("TEMPLATE") == "app") { + QString icon = fileFixify(var("ICON")); + QString bundlePrefix = project->first("QMAKE_TARGET_BUNDLE_PREFIX").toQString(); + if (bundlePrefix.isEmpty()) + bundlePrefix = "com.yourcompany"; + if (bundlePrefix.endsWith(".")) + bundlePrefix.chop(1); + QString bundleIdentifier = bundlePrefix + "." + var("QMAKE_BUNDLE"); + if (bundleIdentifier.endsWith(".app")) + bundleIdentifier.chop(4); + t << "@$(DEL_FILE) " << info_plist_out << "\n\t" + << "@sed "; + foreach (const ProString &arg, commonSedArgs) + t << arg; + t << "-e \"s,@ICON@," << icon.section(Option::dir_sep, -1) << ",g\" " + << "-e \"s,@BUNDLEIDENTIFIER@," << bundleIdentifier << ",g\" " + << "-e \"s,@EXECUTABLE@," << var("QMAKE_ORIG_TARGET") << ",g\" " + << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" " + << "" << info_plist << " >" << info_plist_out << endl; + //copy the icon + if(!project->isEmpty("ICON")) { + QString dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources/"; + const QString icon_path = escapeFilePath(dir + icon.section(Option::dir_sep, -1)); + bundledFiles << icon_path; + t << icon_path << ": " << icon << "\n\t" + << mkdir_p_asstring(dir) << "\n\t" + << "@$(DEL_FILE) " << icon_path << "\n\t" + << "@$(COPY_FILE) " << escapeFilePath(icon) << " " << icon_path << endl; + } + } else { + t << "@$(DEL_FILE) " << info_plist_out << "\n\t" + << "@sed "; + foreach (const ProString &arg, commonSedArgs) + t << arg; + t << "-e \"s,@LIBRARY@," << var("QMAKE_ORIG_TARGET") << ",g\" " + << "-e \"s,@TYPEINFO@," + << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" " + << "" << info_plist << " >" << info_plist_out << endl; + } + //copy other data + if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { + QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/"; + const ProStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA"); + for(int i = 0; i < bundle_data.count(); i++) { + const ProStringList &files = project->values(ProKey(bundle_data[i] + ".files")); + QString path = bundle_dir; + const ProKey vkey(bundle_data[i] + ".version"); + const ProKey pkey(bundle_data[i] + ".path"); + if (!project->isEmpty(vkey)) { + QString version = project->first(vkey) + "/" + + project->first("QMAKE_FRAMEWORK_VERSION") + "/"; + QString link = Option::fixPathToLocalOS(path + project->first(pkey)); + bundledFiles << link; + t << link << ": \n\t" + << mkdir_p_asstring(path) << "\n\t" + << "@$(SYMLINK) " << version << project->first(pkey) << " " << path << endl; + path += version; + } + path += project->first(pkey).toQString(); + path = Option::fixPathToLocalOS(path); + for(int file = 0; file < files.count(); file++) { + QString fn = files.at(file).toQString(); + QString src = fileFixify(fn, FileFixifyAbsolute); + if (!QFile::exists(src)) + src = fn; + src = escapeFilePath(src); + const QString dst = escapeFilePath(path + Option::dir_sep + fileInfo(fn).fileName()); + bundledFiles << dst; + t << dst << ": " << src << "\n\t" + << mkdir_p_asstring(path) << "\n\t"; + QFileInfo fi(fileInfo(fn)); + if(fi.isDir()) + t << "@$(DEL_FILE) -r " << dst << "\n\t" + << "@$(COPY_DIR) " << src << " " << dst << endl; + else + t << "@$(DEL_FILE) " << dst << "\n\t" + << "@$(COPY_FILE) " << src << " " << dst << endl; + } + } + } + } + + ProString ddir; + ProString packageName(project->first("QMAKE_ORIG_TARGET")); + if(!project->isActiveConfig("no_dist_version")) + packageName += var("VERSION"); + if (project->isEmpty("QMAKE_DISTDIR")) + ddir = packageName; + else + ddir = project->first("QMAKE_DISTDIR"); + + QString ddir_c = escapeFilePath(fileFixify((project->isEmpty("OBJECTS_DIR") ? ProString(".tmp/") : + project->first("OBJECTS_DIR")) + ddir, + Option::output_dir, Option::output_dir)); + t << "dist: \n\t" + << mkdir_p_asstring(ddir_c, false) << "\n\t" + << "$(COPY_FILE) --parents $(DIST) " << ddir_c << Option::dir_sep << " && "; + if(!project->isEmpty("QMAKE_EXTRA_COMPILERS")) { + const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS"); + for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) { + const ProStringList &var = project->values(ProKey(*it + ".input")); + for (ProStringList::ConstIterator var_it = var.begin(); var_it != var.end(); ++var_it) { + const ProStringList &val = project->values((*var_it).toKey()); + if(val.isEmpty()) + continue; + t << "$(COPY_FILE) --parents " << val.join(' ') << " " << ddir_c << Option::dir_sep << " && "; + } + } + } + if(!project->isEmpty("TRANSLATIONS")) + t << "$(COPY_FILE) --parents " << var("TRANSLATIONS") << " " << ddir_c << Option::dir_sep << " && "; + t << "(cd `dirname " << ddir_c << "` && " + << "$(TAR) " << packageName << ".tar " << ddir << " && " + << "$(COMPRESS) " << packageName << ".tar) && " + << "$(MOVE) `dirname " << ddir_c << "`" << Option::dir_sep << packageName << ".tar.gz . && " + << "$(DEL_FILE) -r " << ddir_c + << endl << endl; + + t << endl; + + QString clean_targets = "compiler_clean " + var("CLEAN_DEPS"); + if(do_incremental) { + t << "incrclean:\n"; + if(src_incremental) + t << "\t-$(DEL_FILE) $(INCREMENTAL_OBJECTS)\n"; + t << endl; + } + + t << "clean:" << clean_targets << "\n\t"; + if(!project->isEmpty("OBJECTS")) { + if(project->isActiveConfig("compile_libtool")) + t << "-$(LIBTOOL) --mode=clean $(DEL_FILE) $(OBJECTS)\n\t"; + else + t << "-$(DEL_FILE) $(OBJECTS)\n\t"; + } + if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) { + ProStringList precomp_files; + ProString precomph_out_dir; + + if(!project->isEmpty("PRECOMPILED_DIR")) + precomph_out_dir = project->first("PRECOMPILED_DIR"); + precomph_out_dir += project->first("QMAKE_ORIG_TARGET"); + if (!project->isActiveConfig("clang_pch_style")) + precomph_out_dir += project->first("QMAKE_PCH_OUTPUT_EXT"); + + if (project->isActiveConfig("icc_pch_style")) { + // icc style + ProString pchBaseName = project->first("QMAKE_ORIG_TARGET"); + ProString pchOutput; + if(!project->isEmpty("PRECOMPILED_DIR")) + pchOutput = project->first("PRECOMPILED_DIR"); + pchOutput += pchBaseName + project->first("QMAKE_PCH_OUTPUT_EXT"); + ProString sourceFile = pchOutput + Option::cpp_ext.first(); + ProString objectFile = createObjectList(ProStringList(sourceFile)).first(); + + precomp_files << precomph_out_dir << sourceFile << objectFile; + } else { + // gcc style (including clang_pch_style) + precomph_out_dir += Option::dir_sep; + + ProString header_prefix = project->first("QMAKE_PRECOMP_PREFIX"); + ProString header_suffix = project->isActiveConfig("clang_pch_style") + ? project->first("QMAKE_PCH_OUTPUT_EXT") : ""; + + if(!project->isEmpty("QMAKE_CFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "c" + header_suffix; + if(!project->isEmpty("QMAKE_CXXFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "c++" + header_suffix; + if(project->isActiveConfig("objective_c")) { + if(!project->isEmpty("QMAKE_OBJCFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "objective-c" + header_suffix; + if(!project->isEmpty("QMAKE_OBJCXXFLAGS_PRECOMPILE")) + precomp_files += precomph_out_dir + header_prefix + "objective-c++" + header_suffix; + } + } + t << "-$(DEL_FILE) " << precomp_files.join(' ') << "\n\t"; + } + if(!project->isEmpty("IMAGES")) + t << varGlue("QMAKE_IMAGE_COLLECTION", "\t-$(DEL_FILE) ", " ", "") << "\n\t"; + if(src_incremental) + t << "-$(DEL_FILE) $(INCREMENTAL_OBJECTS)\n\t"; + t << varGlue("QMAKE_CLEAN","-$(DEL_FILE) "," ","\n\t") + << "-$(DEL_FILE) *~ core *.core\n" + << varGlue("CLEAN_FILES","\t-$(DEL_FILE) "," ","") << endl << endl; + + ProString destdir = project->first("DESTDIR"); + if (!destdir.isEmpty() && !destdir.endsWith(Option::dir_sep)) + destdir += Option::dir_sep; + t << "distclean: clean " << var("DISTCLEAN_DEPS") << '\n'; + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString bundlePath = escapeFilePath(destdir + project->first("QMAKE_BUNDLE")); + t << "\t-$(DEL_FILE) -r " << bundlePath << endl; + } else if(project->isActiveConfig("compile_libtool")) { + t << "\t-$(LIBTOOL) --mode=clean $(DEL_FILE) $(TARGET)\n"; + } else if(!project->isActiveConfig("staticlib") && project->values("QMAKE_APP_FLAG").isEmpty() && + !project->isActiveConfig("plugin")) { + t << "\t-$(DEL_FILE) " << destdir << "$(TARGET) \n"; + if (!project->isActiveConfig("unversioned_libname")) { + t << "\t-$(DEL_FILE) " << destdir << "$(TARGET0) " << destdir << "$(TARGET1) " + << destdir << "$(TARGET2) $(TARGETA)\n"; + } else { + t << "\t-$(DEL_FILE) $(TARGETA)\n"; + } + } else { + t << "\t-$(DEL_FILE) " << destdir << "$(TARGET) \n"; + } + t << varGlue("QMAKE_DISTCLEAN","\t-$(DEL_FILE) "," ","\n"); + { + QString ofile = Option::fixPathToTargetOS(fileFixify(Option::output.fileName())); + if(!ofile.isEmpty()) + t << "\t-$(DEL_FILE) " << ofile << endl; + } + t << endl << endl; + + t << "####### Sub-libraries\n\n"; + if (!project->values("SUBLIBS").isEmpty()) { + ProString libdir = "tmp/"; + if (!project->isEmpty("SUBLIBS_DIR")) + libdir = project->first("SUBLIBS_DIR"); + const ProStringList &l = project->values("SUBLIBS"); + for (it = l.begin(); it != l.end(); ++it) + t << libdir << project->first("QMAKE_PREFIX_STATICLIB") << (*it) << "." + << project->first("QMAKE_EXTENSION_STATICLIB") << ":\n\t" + << var(ProKey("MAKELIB" + *it)) << endl << endl; + } + + if(doPrecompiledHeaders() && !project->isEmpty("PRECOMPILED_HEADER")) { + QString pchInput = project->first("PRECOMPILED_HEADER").toQString(); + t << "###### Precompiled headers\n"; + QString comps[] = { "C", "CXX", "OBJC", "OBJCXX", QString() }; + for(int i = 0; !comps[i].isNull(); i++) { + QString pchFlags = var(ProKey("QMAKE_" + comps[i] + "FLAGS_PRECOMPILE")); + if(pchFlags.isEmpty()) + continue; + + QString cflags; + if(comps[i] == "OBJC" || comps[i] == "OBJCXX") + cflags += " $(CFLAGS)"; + else + cflags += " $(" + comps[i] + "FLAGS)"; + + ProString pchBaseName = project->first("QMAKE_ORIG_TARGET"); + ProString pchOutput; + if(!project->isEmpty("PRECOMPILED_DIR")) + pchOutput = project->first("PRECOMPILED_DIR"); + pchOutput += pchBaseName; + if (!project->isActiveConfig("clang_pch_style")) + pchOutput += project->first("QMAKE_PCH_OUTPUT_EXT"); + + if (project->isActiveConfig("icc_pch_style")) { + // icc style + QString sourceFile = pchOutput + Option::cpp_ext.first(); + QString objectFile = createObjectList(ProStringList(sourceFile)).first().toQString(); + t << pchOutput << ": " << pchInput << " " << findDependencies(pchInput).join(" \\\n\t\t") + << "\n\techo \"// Automatically generated, do not modify\" > " << sourceFile + << "\n\trm -f " << pchOutput; + + pchFlags = pchFlags.replace("${QMAKE_PCH_TEMP_SOURCE}", sourceFile) + .replace("${QMAKE_PCH_TEMP_OBJECT}", objectFile); + } else { + // gcc style (including clang_pch_style) + ProString header_prefix = project->first("QMAKE_PRECOMP_PREFIX"); + ProString header_suffix = project->isActiveConfig("clang_pch_style") + ? project->first("QMAKE_PCH_OUTPUT_EXT") : ""; + pchOutput += Option::dir_sep; + QString pchOutputDir = pchOutput.toQString(), pchOutputFile; + + if(comps[i] == "C") { + pchOutputFile = "c"; + } else if(comps[i] == "CXX") { + pchOutputFile = "c++"; + } else if(project->isActiveConfig("objective_c")) { + if(comps[i] == "OBJC") + pchOutputFile = "objective-c"; + else if(comps[i] == "OBJCXX") + pchOutputFile = "objective-c++"; + } + if(pchOutputFile.isEmpty()) + continue; + pchOutput += header_prefix + pchOutputFile + header_suffix; + + t << pchOutput << ": " << pchInput << " " << findDependencies(pchInput).join(" \\\n\t\t") + << "\n\t" << mkdir_p_asstring(pchOutputDir); + } + pchFlags = pchFlags.replace("${QMAKE_PCH_INPUT}", pchInput) + .replace("${QMAKE_PCH_OUTPUT_BASE}", pchBaseName.toQString()) + .replace("${QMAKE_PCH_OUTPUT}", pchOutput.toQString()); + + QString compiler; + if(comps[i] == "C" || comps[i] == "OBJC" || comps[i] == "OBJCXX") + compiler = "$(CC)"; + else + compiler = "$(CXX)"; + + // compile command + t << "\n\t" << compiler << cflags << " $(INCPATH) " << pchFlags << endl << endl; + } + } + + writeExtraTargets(t); + writeExtraCompilerTargets(t); +} + +void UnixMakefileGenerator::init2() +{ + if(project->isEmpty("QMAKE_FRAMEWORK_VERSION")) + project->values("QMAKE_FRAMEWORK_VERSION").append(project->values("VER_MAJ").first()); + + if (project->values("TEMPLATE").first() == "aux") + return; + + if (!project->values("QMAKE_APP_FLAG").isEmpty()) { + if(!project->isEmpty("QMAKE_BUNDLE")) { + ProString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION"); + if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/")) + bundle_loc.prepend("/"); + if(!bundle_loc.endsWith("/")) + bundle_loc += "/"; + project->values("TARGET").first().prepend(project->first("QMAKE_BUNDLE") + bundle_loc); + } + if(!project->isEmpty("TARGET")) + project->values("TARGET").first().prepend(project->first("DESTDIR")); + if (!project->values("QMAKE_CYGWIN_EXE").isEmpty()) + project->values("TARGET_EXT").append(".exe"); + } else if (project->isActiveConfig("staticlib")) { + project->values("TARGET").first().prepend(project->first("QMAKE_PREFIX_STATICLIB")); + project->values("TARGET").first() += "." + project->first("QMAKE_EXTENSION_STATICLIB"); + if(project->values("QMAKE_AR_CMD").isEmpty()) + project->values("QMAKE_AR_CMD").append("$(AR) $(TARGET) $(OBJECTS)"); + } else { + project->values("TARGETA").append(project->first("DESTDIR") + project->first("QMAKE_PREFIX_STATICLIB") + + project->first("TARGET") + "." + project->first("QMAKE_EXTENSION_STATICLIB")); + if(project->isActiveConfig("compile_libtool")) + project->values("TARGET_la") = ProStringList(project->first("DESTDIR") + "lib" + project->first("TARGET") + Option::libtool_ext); + + ProStringList &ar_cmd = project->values("QMAKE_AR_CMD"); + if (!ar_cmd.isEmpty()) + ar_cmd[0] = ar_cmd.at(0).toQString().replace("(TARGET)","(TARGETA)"); + else + ar_cmd.append("$(AR) $(TARGETA) $(OBJECTS)"); + if(project->isActiveConfig("compile_libtool")) { + project->values("TARGET") = project->values("TARGET_la"); + } else if(!project->isEmpty("QMAKE_BUNDLE")) { + ProString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION"); + if(!bundle_loc.isEmpty() && !bundle_loc.startsWith("/")) + bundle_loc.prepend("/"); + if(!bundle_loc.endsWith("/")) + bundle_loc += "/"; + project->values("TARGET_").append(project->first("QMAKE_BUNDLE") + + bundle_loc + unescapeFilePath(project->first("TARGET"))); + project->values("TARGET_x.y").append(project->first("QMAKE_BUNDLE") + + "/Versions/" + + project->first("QMAKE_FRAMEWORK_VERSION") + + bundle_loc + unescapeFilePath(project->first("TARGET"))); + } else if(project->isActiveConfig("plugin")) { + QString prefix; + if(!project->isActiveConfig("no_plugin_name_prefix")) + prefix = "lib"; + project->values("TARGET_x.y.z").append(prefix + + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_PLUGIN")); + if(project->isActiveConfig("lib_version_first")) + project->values("TARGET_x").append(prefix + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_PLUGIN")); + else + project->values("TARGET_x").append(prefix + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_PLUGIN") + + "." + project->first("VER_MAJ")); + project->values("TARGET") = project->values("TARGET_x.y.z"); + } else if (!project->isEmpty("QMAKE_HPUX_SHLIB")) { + project->values("TARGET_").append("lib" + project->first("TARGET") + ".sl"); + if(project->isActiveConfig("lib_version_first")) + project->values("TARGET_x").append("lib" + project->first("VER_MAJ") + "." + + project->first("TARGET")); + else + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ")); + project->values("TARGET") = project->values("TARGET_x"); + } else if (!project->isEmpty("QMAKE_AIX_SHLIB")) { + project->values("TARGET_").append(project->first("QMAKE_PREFIX_STATICLIB") + project->first("TARGET") + + "." + project->first("QMAKE_EXTENSION_STATICLIB")); + if(project->isActiveConfig("lib_version_first")) { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + + "." + project->first("VER_MIN") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + } else { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ") + + "." + project->first("VER_MIN")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + project->values("TARGET") = project->values("TARGET_x.y.z"); + } else { + project->values("TARGET_").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + if(project->isActiveConfig("lib_version_first")) { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + + "." + project->first("VER_MIN") + "." + + project->first("QMAKE_EXTENSION_SHLIB")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT") + "." + + project->values("QMAKE_EXTENSION_SHLIB").first()); + } else { + project->values("TARGET_x").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ")); + project->values("TARGET_x.y").append("lib" + project->first("TARGET") + "." + + project->first("QMAKE_EXTENSION_SHLIB") + + "." + project->first("VER_MAJ") + + "." + project->first("VER_MIN")); + project->values("TARGET_x.y.z").append("lib" + project->first("TARGET") + + "." + + project->values( + "QMAKE_EXTENSION_SHLIB").first() + "." + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + if (project->isActiveConfig("unversioned_libname")) + project->values("TARGET") = project->values("TARGET_"); + else + project->values("TARGET") = project->values("TARGET_x.y.z"); + } + if(project->isEmpty("QMAKE_LN_SHLIB")) + project->values("QMAKE_LN_SHLIB").append("ln -s"); + if (!project->values("QMAKE_LFLAGS_SONAME").isEmpty()) { + ProString soname; + if(project->isActiveConfig("plugin")) { + if(!project->values("TARGET").isEmpty()) + soname += project->first("TARGET"); + } else if(!project->isEmpty("QMAKE_BUNDLE")) { + soname += project->first("TARGET_x.y"); + } else if(project->isActiveConfig("unversioned_soname")) { + soname = "lib" + project->first("QMAKE_ORIG_TARGET") + + "." + project->first("QMAKE_EXTENSION_SHLIB"); + } else if(!project->values("TARGET_x").isEmpty()) { + soname += project->first("TARGET_x"); + } + if(!soname.isEmpty()) { + if(project->isActiveConfig("absolute_library_soname") && + project->values("INSTALLS").indexOf("target") != -1 && + !project->isEmpty("target.path")) { + QString instpath = Option::fixPathToTargetOS(project->first("target.path").toQString()); + if(!instpath.endsWith(Option::dir_sep)) + instpath += Option::dir_sep; + soname.prepend(instpath); + } + project->values("QMAKE_LFLAGS_SONAME").first() += escapeFilePath(soname); + } + } + if (project->values("QMAKE_LINK_SHLIB_CMD").isEmpty()) + project->values("QMAKE_LINK_SHLIB_CMD").append( + "$(LINK) $(LFLAGS) " + project->first("QMAKE_LINK_O_FLAG") + "$(TARGET) $(OBJECTS) $(LIBS) $(OBJCOMP)"); + } + if (!project->values("QMAKE_APP_FLAG").isEmpty()) { + project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_APP"); + project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_APP"); + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_APP"); + } else if (project->isActiveConfig("dll")) { + if(!project->isActiveConfig("plugin") || !project->isActiveConfig("plugin_no_share_shlib_cflags")) { + project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_SHLIB"); + project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_SHLIB"); + } + if (project->isActiveConfig("plugin")) { + project->values("QMAKE_CFLAGS") += project->values("QMAKE_CFLAGS_PLUGIN"); + project->values("QMAKE_CXXFLAGS") += project->values("QMAKE_CXXFLAGS_PLUGIN"); + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_PLUGIN"); + if(project->isActiveConfig("plugin_with_soname") && !project->isActiveConfig("compile_libtool")) + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SONAME"); + } else { + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SHLIB"); + if(!project->isEmpty("QMAKE_LFLAGS_COMPAT_VERSION")) { + if(project->isEmpty("COMPAT_VERSION")) + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_COMPAT_VERSION") + + project->first("VER_MAJ") + "." + + project->first("VER_MIN")); + else + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_COMPAT_VERSION") + + project->first("COMPATIBILITY_VERSION")); + } + if(!project->isEmpty("QMAKE_LFLAGS_VERSION")) { + project->values("QMAKE_LFLAGS") += QString(project->first("QMAKE_LFLAGS_VERSION") + + project->first("VER_MAJ") + "." + + project->first("VER_MIN") + "." + + project->first("VER_PAT")); + } + if(!project->isActiveConfig("compile_libtool")) + project->values("QMAKE_LFLAGS") += project->values("QMAKE_LFLAGS_SONAME"); + } + } + + if (include_deps && project->isActiveConfig("gcc_MD_depends")) { + // use -MMD if we know about -isystem too + ProString MD_flag(project->values("QMAKE_CFLAGS_ISYSTEM").isEmpty() ? "-MD" : "-MMD"); + project->values("QMAKE_CFLAGS") += MD_flag; + project->values("QMAKE_CXXFLAGS") += MD_flag; + } + + if(!project->isEmpty("QMAKE_BUNDLE")) { + QString plist = fileFixify(project->first("QMAKE_INFO_PLIST").toQString(), qmake_getpwd()); + if(plist.isEmpty()) + plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"); + if(exists(Option::fixPathToLocalOS(plist))) { + project->values("QMAKE_INFO_PLIST_OUT").append(project->first("DESTDIR") + + project->first("QMAKE_BUNDLE") + + "/Contents/Info.plist"); + project->values("ALL_DEPS") += project->first("QMAKE_INFO_PLIST_OUT"); + if(!project->isEmpty("ICON") && project->first("TEMPLATE") == "app") + project->values("ALL_DEPS") += project->first("DESTDIR") + + project->first("QMAKE_BUNDLE") + + "/Contents/Resources/" + project->first("ICON").toQString().section('/', -1); + if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { + QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/"; + ProStringList &alldeps = project->values("ALL_DEPS"); + const ProStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA"); + for(int i = 0; i < bundle_data.count(); i++) { + const ProStringList &files = project->values(ProKey(bundle_data[i] + ".files")); + QString path = bundle_dir; + const ProKey vkey(bundle_data[i] + ".version"); + const ProKey pkey(bundle_data[i] + ".path"); + if (!project->isEmpty(vkey)) { + alldeps += Option::fixPathToLocalOS(path + Option::dir_sep + project->first(pkey)); + path += project->first(vkey) + "/" + + project->first("QMAKE_FRAMEWORK_VERSION") + "/"; + } + path += project->first(pkey); + path = Option::fixPathToLocalOS(path); + for(int file = 0; file < files.count(); file++) + alldeps += path + Option::dir_sep + fileInfo(files[file].toQString()).fileName(); + } + } + } else { + warn_msg(WarnLogic, "Could not resolve Info.plist: '%s'. Check if QMAKE_INFO_PLIST points to a valid file.", plist.toLatin1().constData()); + } + } +} + +QString +UnixMakefileGenerator::libtoolFileName(bool fixify) +{ + QString ret = var("TARGET"); + int slsh = ret.lastIndexOf(Option::dir_sep); + if(slsh != -1) + ret = ret.right(ret.length() - slsh - 1); + int dot = ret.indexOf('.'); + if(dot != -1) + ret = ret.left(dot); + ret += Option::libtool_ext; + if(!project->isEmpty("QMAKE_LIBTOOL_DESTDIR")) + ret.prepend(project->first("QMAKE_LIBTOOL_DESTDIR") + Option::dir_sep); + if(fixify) { + if(QDir::isRelativePath(ret) && !project->isEmpty("DESTDIR")) + ret.prepend(project->first("DESTDIR").toQString()); + ret = Option::fixPathToLocalOS(fileFixify(ret, qmake_getpwd(), Option::output_dir)); + } + return ret; +} + +void +UnixMakefileGenerator::writeLibtoolFile() +{ + QString fname = libtoolFileName(), lname = fname; + mkdir(fileInfo(fname).path()); + int slsh = lname.lastIndexOf(Option::dir_sep); + if(slsh != -1) + lname = lname.right(lname.length() - slsh - 1); + QFile ft(fname); + if(!ft.open(QIODevice::WriteOnly)) + return; + project->values("ALL_DEPS").append(fileFixify(fname)); + + QTextStream t(&ft); + t << "# " << lname << " - a libtool library file\n"; + t << "# Generated by qmake/libtool (" QMAKE_VERSION_STR ") (Qt " + << QT_VERSION_STR << ")"; + t << "\n"; + + t << "# The name that we can dlopen(3).\n" + << "dlname='" << var(project->isActiveConfig("plugin") ? "TARGET" : "TARGET_x") + << "'\n\n"; + + t << "# Names of this library.\n"; + t << "library_names='"; + if(project->isActiveConfig("plugin")) { + t << var("TARGET"); + } else { + if (project->isEmpty("QMAKE_HPUX_SHLIB")) + t << var("TARGET_x.y.z") << " "; + t << var("TARGET_x") << " " << var("TARGET_"); + } + t << "'\n\n"; + + t << "# The name of the static archive.\n" + << "old_library='" << lname.left(lname.length()-Option::libtool_ext.length()) << ".a'\n\n"; + + t << "# Libraries that this one depends upon.\n"; + ProStringList libs; + if(!project->isEmpty("QMAKE_INTERNAL_PRL_LIBS")) + libs = project->values("QMAKE_INTERNAL_PRL_LIBS"); + else + libs << "QMAKE_LIBS"; //obvious one + t << "dependency_libs='"; + for (ProStringList::ConstIterator it = libs.begin(); it != libs.end(); ++it) + t << project->values((*it).toKey()).join(' ') << " "; + t << "'\n\n"; + + t << "# Version information for " << lname << "\n"; + int maj = project->first("VER_MAJ").toInt(); + int min = project->first("VER_MIN").toInt(); + int pat = project->first("VER_PAT").toInt(); + t << "current=" << (10*maj + min) << "\n" // best I can think of + << "age=0\n" + << "revision=" << pat << "\n\n"; + + t << "# Is this an already installed library.\n" + "installed=yes\n\n"; // ### + + t << "# Files to dlopen/dlpreopen.\n" + "dlopen=''\n" + "dlpreopen=''\n\n"; + + ProString install_dir = project->first("QMAKE_LIBTOOL_LIBDIR"); + if(install_dir.isEmpty()) + install_dir = project->first("target.path"); + if(install_dir.isEmpty()) + install_dir = project->first("DESTDIR"); + t << "# Directory that this library needs to be installed in:\n" + "libdir='" << Option::fixPathToTargetOS(install_dir.toQString(), false) << "'\n"; +} + +QT_END_NAMESPACE diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/corelib/arch/qatomic_bootstrap.h qt-everywhere-opensource-src-5.3.1.new/qtbase/src/corelib/arch/qatomic_bootstrap.h --- qt-everywhere-opensource-src-5.3.1/qtbase/src/corelib/arch/qatomic_bootstrap.h 2014-06-19 12:08:07.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/corelib/arch/qatomic_bootstrap.h 2014-08-13 04:35:08.404435894 +0200 @@ -67,8 +67,10 @@ return --_q_value != 0; } - static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW + static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW { + if (currentValue) + *currentValue = _q_value; if (_q_value == expectedValue) { _q_value = newValue; return true; diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/corelib/io/qloggingcategory.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/corelib/io/qloggingcategory.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/corelib/io/qloggingcategory.cpp 2014-06-19 12:08:07.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/corelib/io/qloggingcategory.cpp 2014-08-13 04:35:08.404435894 +0200 @@ -50,6 +50,18 @@ Q_GLOBAL_STATIC_WITH_ARGS(QLoggingCategory, qtDefaultCategory, (qtDefaultCategoryName)) +#ifndef Q_ATOMIC_INT8_IS_SUPPORTED +static void setBoolLane(QBasicAtomicInt *atomic, bool enable, int shift) +{ + const int bit = 1 << shift; + + if (enable) + atomic->fetchAndOrRelaxed(bit); + else + atomic->fetchAndAndRelaxed(~bit); +} +#endif + /*! \class QLoggingCategory \inmodule QtCore @@ -171,13 +183,11 @@ */ QLoggingCategory::QLoggingCategory(const char *category) : d(0), - name(0), - enabledDebug(true), - enabledWarning(true), - enabledCritical(true) + name(0) { Q_UNUSED(d); Q_UNUSED(placeholder); + enabled.store(0x01010101); // enabledDebug = enabledWarning = enabledCritical = true; const bool isDefaultCategory = (category == 0) || (strcmp(category, qtDefaultCategoryName) == 0); @@ -249,9 +259,9 @@ bool QLoggingCategory::isEnabled(QtMsgType msgtype) const { switch (msgtype) { - case QtDebugMsg: return enabledDebug; - case QtWarningMsg: return enabledWarning; - case QtCriticalMsg: return enabledCritical; + case QtDebugMsg: return isDebugEnabled(); + case QtWarningMsg: return isWarningEnabled(); + case QtCriticalMsg: return isCriticalEnabled(); case QtFatalMsg: return true; } return false; @@ -270,9 +280,15 @@ void QLoggingCategory::setEnabled(QtMsgType type, bool enable) { switch (type) { - case QtDebugMsg: enabledDebug = enable; break; - case QtWarningMsg: enabledWarning = enable; break; - case QtCriticalMsg: enabledCritical = enable; break; +#ifdef Q_ATOMIC_INT8_IS_SUPPORTED + case QtDebugMsg: bools.enabledDebug.store(enable); break; + case QtWarningMsg: bools.enabledWarning.store(enable); break; + case QtCriticalMsg: bools.enabledCritical.store(enable); break; +#else + case QtDebugMsg: setBoolLane(&enabled, enable, DebugShift); break; + case QtWarningMsg: setBoolLane(&enabled, enable, WarningShift); break; + case QtCriticalMsg: setBoolLane(&enabled, enable, CriticalShift); break; +#endif case QtFatalMsg: break; } } diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/corelib/io/qloggingcategory.h qt-everywhere-opensource-src-5.3.1.new/qtbase/src/corelib/io/qloggingcategory.h --- qt-everywhere-opensource-src-5.3.1/qtbase/src/corelib/io/qloggingcategory.h 2014-06-19 12:08:07.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/corelib/io/qloggingcategory.h 2014-08-13 04:35:08.405435896 +0200 @@ -57,10 +57,15 @@ bool isEnabled(QtMsgType type) const; void setEnabled(QtMsgType type, bool enable); - bool isDebugEnabled() const { return enabledDebug; } - bool isWarningEnabled() const { return enabledWarning; } - bool isCriticalEnabled() const { return enabledCritical; } - +#ifdef Q_ATOMIC_INT8_IS_SUPPORTED + bool isDebugEnabled() const { return bools.enabledDebug.load(); } + bool isWarningEnabled() const { return bools.enabledWarning.load(); } + bool isCriticalEnabled() const { return bools.enabledCritical.load(); } +#else + bool isDebugEnabled() const { return enabled.load() >> DebugShift & 1; } + bool isWarningEnabled() const { return enabled.load() >> WarningShift & 1; } + bool isCriticalEnabled() const { return enabled.load() >> CriticalShift & 1; } +#endif const char *categoryName() const { return name; } // allows usage of both factory method and variable in qCX macros @@ -78,10 +83,24 @@ void *d; // reserved for future use const char *name; - bool enabledDebug; - bool enabledWarning; - bool enabledCritical; - bool placeholder[5]; // reserve for future use +#ifdef Q_BIG_ENDIAN + enum { DebugShift = 0, WarningShift = 8, CriticalShift = 16 }; +#else + enum { DebugShift = 24, WarningShift = 16, CriticalShift = 8 }; +#endif + + struct AtomicBools { +#ifdef Q_ATOMIC_INT8_IS_SUPPORTED + QBasicAtomicInteger enabledDebug; + QBasicAtomicInteger enabledWarning; + QBasicAtomicInteger enabledCritical; +#endif + }; + union { + AtomicBools bools; + QBasicAtomicInt enabled; + }; + bool placeholder[4]; // reserve for future use }; #define Q_DECLARE_LOGGING_CATEGORY(name) \ diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/dbus/qdbusintegrator.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/dbus/qdbusintegrator.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/dbus/qdbusintegrator.cpp 2014-06-19 12:08:02.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/dbus/qdbusintegrator.cpp 2014-08-13 04:35:08.409435892 +0200 @@ -73,18 +73,24 @@ QT_BEGIN_NAMESPACE -static bool isDebugging; -#define qDBusDebug if (!::isDebugging); else qDebug +static QBasicAtomicInt isDebugging = Q_BASIC_ATOMIC_INITIALIZER(-1); +#define qDBusDebug if (::isDebugging == 0); else qDebug -Q_GLOBAL_STATIC_WITH_ARGS(const QString, orgFreedesktopDBusString, (QLatin1String(DBUS_SERVICE_DBUS))) +QString orgFreedesktopDBusString() +{ + return QStringLiteral(DBUS_SERVICE_DBUS); +} static inline QString dbusServiceString() -{ return *orgFreedesktopDBusString(); } +{ + return orgFreedesktopDBusString(); +} + static inline QString dbusInterfaceString() { // it's the same string, but just be sure - Q_ASSERT(*orgFreedesktopDBusString() == QLatin1String(DBUS_INTERFACE_DBUS)); - return *orgFreedesktopDBusString(); + Q_ASSERT(orgFreedesktopDBusString() == QLatin1String(DBUS_INTERFACE_DBUS)); + return orgFreedesktopDBusString(); } static inline QDebug operator<<(QDebug dbg, const QThread *th) @@ -1022,13 +1028,12 @@ anonymousAuthenticationAllowed(false) { static const bool threads = q_dbus_threads_init_default(); - static const int debugging = qgetenv("QDBUS_DEBUG").toInt(); - ::isDebugging = debugging; + if (::isDebugging == -1) + ::isDebugging = qgetenv("QDBUS_DEBUG").toInt(); Q_UNUSED(threads) - Q_UNUSED(debugging) #ifdef QDBUS_THREAD_DEBUG - if (debugging > 1) + if (::isDebugging > 1) qdbusThreadDebug = qdbusDefaultThreadDebug; #endif @@ -2267,7 +2272,7 @@ watchedServices.erase(sit); disconnectSignal(dbusServiceString(), QString(), dbusInterfaceString(), QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(), - this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); + this, SLOT(serviceOwnerChangedNoLock(QString,QString,QString))); } } } diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/dbus/qdbusintegrator.cpp.orig qt-everywhere-opensource-src-5.3.1.new/qtbase/src/dbus/qdbusintegrator.cpp.orig --- qt-everywhere-opensource-src-5.3.1/qtbase/src/dbus/qdbusintegrator.cpp.orig 1970-01-01 01:00:00.000000000 +0100 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/dbus/qdbusintegrator.cpp.orig 2014-08-13 04:35:08.398435926 +0200 @@ -0,0 +1,2517 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusintegrator_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qdbusargument.h" +#include "qdbusconnection_p.h" +#include "qdbusconnectionmanager_p.h" +#include "qdbusinterface_p.h" +#include "qdbusmessage.h" +#include "qdbusmetatype.h" +#include "qdbusmetatype_p.h" +#include "qdbusabstractadaptor.h" +#include "qdbusabstractadaptor_p.h" +#include "qdbusutil_p.h" +#include "qdbusvirtualobject.h" +#include "qdbusmessage_p.h" +#include "qdbuscontext_p.h" +#include "qdbuspendingcall_p.h" + +#include "qdbusthreaddebug_p.h" + +#include + +#ifndef QT_NO_DBUS + +QT_BEGIN_NAMESPACE + +static bool isDebugging; +#define qDBusDebug if (!::isDebugging); else qDebug + +QString orgFreedesktopDBusString() +{ + return QStringLiteral(DBUS_SERVICE_DBUS); +} + +static inline QString dbusServiceString() +{ + return orgFreedesktopDBusString(); +} + +static inline QString dbusInterfaceString() +{ + // it's the same string, but just be sure + Q_ASSERT(orgFreedesktopDBusString() == QLatin1String(DBUS_INTERFACE_DBUS)); + return orgFreedesktopDBusString(); +} + +static inline QDebug operator<<(QDebug dbg, const QThread *th) +{ + dbg.nospace() << "QThread(ptr=" << (void*)th; + if (th && !th->objectName().isEmpty()) + dbg.nospace() << ", name=" << th->objectName(); + dbg.nospace() << ')'; + return dbg.space(); +} + +#if QDBUS_THREAD_DEBUG +static inline QDebug operator<<(QDebug dbg, const QDBusConnectionPrivate *conn) +{ + dbg.nospace() << "QDBusConnection(" + << "ptr=" << (void*)conn + << ", name=" << conn->name + << ", baseService=" << conn->baseService + << ", thread="; + if (conn->thread() == QThread::currentThread()) + dbg.nospace() << "same thread"; + else + dbg.nospace() << conn->thread(); + dbg.nospace() << ')'; + return dbg.space(); +} + +void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate *conn) +{ + qDBusDebug() << QThread::currentThread() + << "Qt D-Bus threading action" << action + << (condition == QDBusLockerBase::BeforeLock ? "before lock" : + condition == QDBusLockerBase::AfterLock ? "after lock" : + condition == QDBusLockerBase::BeforeUnlock ? "before unlock" : + condition == QDBusLockerBase::AfterUnlock ? "after unlock" : + condition == QDBusLockerBase::BeforePost ? "before event posting" : + condition == QDBusLockerBase::AfterPost ? "after event posting" : + condition == QDBusLockerBase::BeforeDeliver ? "before event delivery" : + condition == QDBusLockerBase::AfterDeliver ? "after event delivery" : + condition == QDBusLockerBase::BeforeAcquire ? "before acquire" : + condition == QDBusLockerBase::AfterAcquire ? "after acquire" : + condition == QDBusLockerBase::BeforeRelease ? "before release" : + condition == QDBusLockerBase::AfterRelease ? "after release" : + "condition unknown") + << "in connection" << conn; +} +qdbusThreadDebugFunc qdbusThreadDebug = 0; +#endif + +typedef void (*QDBusSpyHook)(const QDBusMessage&); +typedef QVarLengthArray QDBusSpyHookList; +Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList) + +extern "C" { + + // libdbus-1 callbacks + +static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms); +static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + // qDebug("addTimeout %d", q_dbus_timeout_get_interval(timeout)); + + QDBusConnectionPrivate *d = static_cast(data); + + if (!q_dbus_timeout_get_enabled(timeout)) + return true; + + QDBusWatchAndTimeoutLocker locker(AddTimeoutAction, d); + if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { + // correct thread + return qDBusRealAddTimeout(d, timeout, q_dbus_timeout_get_interval(timeout)); + } else { + // wrong thread: sync back + QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; + ev->subtype = QDBusConnectionCallbackEvent::AddTimeout; + d->timeoutsPendingAdd.append(qMakePair(timeout, q_dbus_timeout_get_interval(timeout))); + d->postEventToThread(AddTimeoutAction, d, ev); + return true; + } +} + +static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms) +{ + Q_ASSERT(d->timeouts.keys(timeout).isEmpty()); + + int timerId = d->startTimer(ms); + if (!timerId) + return false; + + d->timeouts[timerId] = timeout; + return true; +} + +static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + // qDebug("removeTimeout"); + + QDBusConnectionPrivate *d = static_cast(data); + + QDBusWatchAndTimeoutLocker locker(RemoveTimeoutAction, d); + + // is it pending addition? + QDBusConnectionPrivate::PendingTimeoutList::iterator pit = d->timeoutsPendingAdd.begin(); + while (pit != d->timeoutsPendingAdd.end()) { + if (pit->first == timeout) + pit = d->timeoutsPendingAdd.erase(pit); + else + ++pit; + } + + // is it a running timer? + bool correctThread = QCoreApplication::instance() && QThread::currentThread() == d->thread(); + QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin(); + while (it != d->timeouts.end()) { + if (it.value() == timeout) { + if (correctThread) { + // correct thread + d->killTimer(it.key()); + } else { + // incorrect thread or no application, post an event for later + QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; + ev->subtype = QDBusConnectionCallbackEvent::KillTimer; + ev->timerId = it.key(); + d->postEventToThread(KillTimerAction, d, ev); + } + it = d->timeouts.erase(it); + break; + } else { + ++it; + } + } +} + +static void qDBusToggleTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + //qDebug("ToggleTimeout"); + + qDBusRemoveTimeout(timeout, data); + qDBusAddTimeout(timeout, data); +} + +static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd); +static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + QDBusConnectionPrivate *d = static_cast(data); + + int flags = q_dbus_watch_get_flags(watch); + int fd = q_dbus_watch_get_unix_fd(watch); + + if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { + return qDBusRealAddWatch(d, watch, flags, fd); + } else { + QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; + ev->subtype = QDBusConnectionCallbackEvent::AddWatch; + ev->watch = watch; + ev->fd = fd; + ev->extra = flags; + d->postEventToThread(AddWatchAction, d, ev); + return true; + } +} + +static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd) +{ + QDBusConnectionPrivate::Watcher watcher; + + QDBusWatchAndTimeoutLocker locker(AddWatchAction, d); + if (flags & DBUS_WATCH_READABLE) { + //qDebug("addReadWatch %d", fd); + watcher.watch = watch; + if (QCoreApplication::instance()) { + watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d); + watcher.read->setEnabled(q_dbus_watch_get_enabled(watch)); + d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int))); + } + } + if (flags & DBUS_WATCH_WRITABLE) { + //qDebug("addWriteWatch %d", fd); + watcher.watch = watch; + if (QCoreApplication::instance()) { + watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d); + watcher.write->setEnabled(q_dbus_watch_get_enabled(watch)); + d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int))); + } + } + d->watchers.insertMulti(fd, watcher); + + return true; +} + +static void qDBusRemoveWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + //qDebug("remove watch"); + + QDBusConnectionPrivate *d = static_cast(data); + int fd = q_dbus_watch_get_unix_fd(watch); + + QDBusWatchAndTimeoutLocker locker(RemoveWatchAction, d); + QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); + while (i != d->watchers.end() && i.key() == fd) { + if (i.value().watch == watch) { + if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { + // correct thread, delete the socket notifiers + delete i.value().read; + delete i.value().write; + } else { + // incorrect thread or no application, use delete later + if (i->read) + i->read->deleteLater(); + if (i->write) + i->write->deleteLater(); + } + i = d->watchers.erase(i); + } else { + ++i; + } + } +} + +static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd); +static void qDBusToggleWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + QDBusConnectionPrivate *d = static_cast(data); + int fd = q_dbus_watch_get_unix_fd(watch); + + if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { + qDBusRealToggleWatch(d, watch, fd); + } else { + QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; + ev->subtype = QDBusConnectionCallbackEvent::ToggleWatch; + ev->watch = watch; + ev->fd = fd; + d->postEventToThread(ToggleWatchAction, d, ev); + } +} + +static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd) +{ + QDBusWatchAndTimeoutLocker locker(ToggleWatchAction, d); + + QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); + while (i != d->watchers.end() && i.key() == fd) { + if (i.value().watch == watch) { + bool enabled = q_dbus_watch_get_enabled(watch); + int flags = q_dbus_watch_get_flags(watch); + + //qDebug("toggle watch %d to %d (write: %d, read: %d)", q_dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE); + + if (flags & DBUS_WATCH_READABLE && i.value().read) + i.value().read->setEnabled(enabled); + if (flags & DBUS_WATCH_WRITABLE && i.value().write) + i.value().write->setEnabled(enabled); + return; + } + ++i; + } +} + +static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data) +{ + Q_ASSERT(connection); + Q_UNUSED(connection); + QDBusConnectionPrivate *d = static_cast(data); + + static int slotId; // 0 is QObject::deleteLater() + if (!slotId) { + // it's ok to do this: there's no race condition because the store is atomic + // and we always set to the same value + slotId = QDBusConnectionPrivate::staticMetaObject.indexOfSlot("doDispatch()"); + } + + //qDBusDebug() << "Updating dispatcher status" << slotId; + if (new_status == DBUS_DISPATCH_DATA_REMAINS) + QDBusConnectionPrivate::staticMetaObject.method(slotId). + invoke(d, Qt::QueuedConnection); +} + +static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data) +{ + // ### We may want to separate the server from the QDBusConnectionPrivate + Q_ASSERT(server); Q_UNUSED(server); + Q_ASSERT(connection); + Q_ASSERT(data); + + // keep the connection alive + q_dbus_connection_ref(connection); + QDBusConnectionPrivate *serverConnection = static_cast(data); + + // allow anonymous authentication + if (serverConnection->anonymousAuthenticationAllowed) + q_dbus_connection_set_allow_anonymous(connection, true); + + QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate(serverConnection->parent()); + QMutexLocker locker(&QDBusConnectionManager::instance()->mutex); + QDBusConnectionManager::instance()->setConnection(QLatin1String("QDBusServer-") + QString::number(reinterpret_cast(newConnection)), newConnection); + serverConnection->serverConnectionNames << newConnection->name; + + // setPeer does the error handling for us + QDBusErrorInternal error; + newConnection->setPeer(connection, error); + + QDBusConnection retval = QDBusConnectionPrivate::q(newConnection); + + // make QDBusServer emit the newConnection signal + serverConnection->serverConnection(retval); +} + +} // extern "C" + +static QByteArray buildMatchRule(const QString &service, + const QString &objectPath, const QString &interface, + const QString &member, const QStringList &argMatch, const QString & /*signature*/) +{ + QString result = QLatin1String("type='signal',"); + QString keyValue = QLatin1String("%1='%2',"); + + if (!service.isEmpty()) + result += keyValue.arg(QLatin1String("sender"), service); + if (!objectPath.isEmpty()) + result += keyValue.arg(QLatin1String("path"), objectPath); + if (!interface.isEmpty()) + result += keyValue.arg(QLatin1String("interface"), interface); + if (!member.isEmpty()) + result += keyValue.arg(QLatin1String("member"), member); + + // add the argument string-matching now + if (!argMatch.isEmpty()) { + keyValue = QLatin1String("arg%1='%2',"); + for (int i = 0; i < argMatch.count(); ++i) + if (!argMatch.at(i).isNull()) + result += keyValue.arg(i).arg(argMatch.at(i)); + } + + result.chop(1); // remove ending comma + return result.toLatin1(); +} + +static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root, + const QString &fullpath, int &usedLength, + QDBusConnectionPrivate::ObjectTreeNode &result) +{ + if (!fullpath.compare(QLatin1String("/")) && root->obj) { + usedLength = 1; + result = *root; + return root; + } + int start = 0; + int length = fullpath.length(); + if (fullpath.at(0) == QLatin1Char('/')) + start = 1; + + // walk the object tree + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator node = root; + while (start < length && node) { + if (node->flags & QDBusConnection::ExportChildObjects) + break; + if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath)) + break; + int end = fullpath.indexOf(QLatin1Char('/'), start); + end = (end == -1 ? length : end); + QStringRef pathComponent(&fullpath, start, end - start); + + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = + std::lower_bound(node->children.constBegin(), node->children.constEnd(), pathComponent); + if (it != node->children.constEnd() && it->name == pathComponent) + // match + node = it; + else + node = 0; + + start = end + 1; + } + + // found our object + usedLength = (start > length ? length : start); + if (node) { + if (node->obj || !node->children.isEmpty()) + result = *node; + else + // there really is no object here + // we're just looking at an unused space in the QVector + node = 0; + } + return node; +} + +static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root, + const QString &fullpath, int start) +{ + int length = fullpath.length(); + + // any object in the tree can tell us to switch to its own object tree: + const QDBusConnectionPrivate::ObjectTreeNode *node = root; + if (node && node->flags & QDBusConnection::ExportChildObjects) { + QObject *obj = node->obj; + + while (obj) { + if (start >= length) + // we're at the correct level + return obj; + + int pos = fullpath.indexOf(QLatin1Char('/'), start); + pos = (pos == -1 ? length : pos); + QStringRef pathComponent(&fullpath, start, pos - start); + + const QObjectList children = obj->children(); + + // find a child with the proper name + QObject *next = 0; + QObjectList::ConstIterator it = children.constBegin(); + QObjectList::ConstIterator end = children.constEnd(); + for ( ; it != end; ++it) + if ((*it)->objectName() == pathComponent) { + next = *it; + break; + } + + if (!next) + break; + + obj = next; + start = pos + 1; + } + } + + // object not found + return 0; +} + +static bool shouldWatchService(const QString &service) +{ + return !service.isEmpty() && !service.startsWith(QLatin1Char(':')); +} + +extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook); +void qDBusAddSpyHook(QDBusSpyHook hook) +{ + qDBusSpyHookList()->append(hook); +} + +extern "C" { +static DBusHandlerResult +qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data) +{ + Q_ASSERT(data); + Q_UNUSED(connection); + QDBusConnectionPrivate *d = static_cast(data); + if (d->mode == QDBusConnectionPrivate::InvalidMode) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message, d->capabilities); + qDBusDebug() << d << "got message (signal):" << amsg; + + return d->handleMessage(amsg) ? + DBUS_HANDLER_RESULT_HANDLED : + DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} +} + +bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) +{ + const QDBusSpyHookList *list = qDBusSpyHookList(); + for (int i = 0; i < list->size(); ++i) { + qDBusDebug() << "calling the message spy hook"; + (*(*list)[i])(amsg); + } + + if (!ref.load()) + return false; + + switch (amsg.type()) { + case QDBusMessage::SignalMessage: + handleSignal(amsg); + // if there are any other filters in this DBusConnection, + // let them see the signal too + return false; + case QDBusMessage::MethodCallMessage: + handleObjectCall(amsg); + return true; + case QDBusMessage::ReplyMessage: + case QDBusMessage::ErrorMessage: + case QDBusMessage::InvalidMessage: + return false; // we don't handle those here + } + + return false; +} + +static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack) +{ + QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin(); + + while (it != haystack.children.end()) { + huntAndDestroy(needle, *it); + if (!it->isActive()) + it = haystack.children.erase(it); + else + it++; + } + + if (needle == haystack.obj) { + haystack.obj = 0; + haystack.flags = 0; + } +} + +static void huntAndUnregister(const QStringList &pathComponents, int i, QDBusConnection::UnregisterMode mode, + QDBusConnectionPrivate::ObjectTreeNode *node) +{ + if (pathComponents.count() == i) { + // found it + node->obj = 0; + node->flags = 0; + + if (mode == QDBusConnection::UnregisterTree) { + // clear the sub-tree as well + node->children.clear(); // can't disconnect the objects because we really don't know if they can + // be found somewhere else in the path too + } + } else { + // keep going + QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node->children.end(); + QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = + std::lower_bound(node->children.begin(), end, pathComponents.at(i)); + if (it == end || it->name != pathComponents.at(i)) + return; // node not found + + huntAndUnregister(pathComponents, i + 1, mode, it); + if (!it->isActive()) + node->children.erase(it); + } +} + +static void huntAndEmit(DBusConnection *connection, DBusMessage *msg, + QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack, + bool isScriptable, bool isAdaptor, const QString &path = QString()) +{ + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin(); + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd(); + for ( ; it != end; ++it) { + if (it->isActive()) + huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1Char('/') + it->name); + } + + if (needle == haystack.obj) { + // is this a signal we should relay? + if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0) + return; // no: it comes from an adaptor and we're not exporting adaptors + else if (!isAdaptor) { + int mask = isScriptable + ? QDBusConnection::ExportScriptableSignals + : QDBusConnection::ExportNonScriptableSignals; + if ((haystack.flags & mask) == 0) + return; // signal was not exported + } + + QByteArray p = path.toLatin1(); + if (p.isEmpty()) + p = "/"; + qDBusDebug() << QThread::currentThread() << "emitting signal at" << p; + DBusMessage *msg2 = q_dbus_message_copy(msg); + q_dbus_message_set_path(msg2, p); + q_dbus_connection_send(connection, msg2, 0); + q_dbus_message_unref(msg2); + } +} + +static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, + const QString &signature_, QVector &metaTypes) +{ + QByteArray msgSignature = signature_.toLatin1(); + + for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) { + QMetaMethod mm = mo->method(idx); + + // check access: + if (mm.access() != QMetaMethod::Public) + continue; + + // check type: + if (mm.methodType() != QMetaMethod::Slot && mm.methodType() != QMetaMethod::Method) + continue; + + // check name: + if (mm.name() != name) + continue; + + int returnType = mm.returnType(); + bool isAsync = qDBusCheckAsyncTag(mm.tag()); + bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; + + // consistency check: + if (isAsync && returnType != QMetaType::Void) + continue; + + QString errorMsg; + int inputCount = qDBusParametersForMethod(mm, metaTypes, errorMsg); + if (inputCount == -1) + continue; // problem parsing + + metaTypes[0] = returnType; + bool hasMessage = false; + if (inputCount > 0 && + metaTypes.at(inputCount) == QDBusMetaTypeId::message()) { + // "no input parameters" is allowed as long as the message meta type is there + hasMessage = true; + --inputCount; + } + + // try to match the parameters + int i; + QByteArray reconstructedSignature; + for (i = 1; i <= inputCount; ++i) { + const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) ); + if (!typeSignature) + break; // invalid + + reconstructedSignature += typeSignature; + if (!msgSignature.startsWith(reconstructedSignature)) + break; + } + + if (reconstructedSignature != msgSignature) + continue; // we didn't match them all + + if (hasMessage) + ++i; + + // make sure that the output parameters have signatures too + if (returnType != QMetaType::UnknownType && returnType != QMetaType::Void && QDBusMetaType::typeToSignature(returnType) == 0) + continue; + + bool ok = true; + for (int j = i; ok && j < metaTypes.count(); ++j) + if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == 0) + ok = false; + if (!ok) + continue; + + // consistency check: + if (isAsync && metaTypes.count() > i + 1) + continue; + + if (mm.methodType() == QMetaMethod::Slot) { + if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0) + continue; // scriptable slots not exported + if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0) + continue; // non-scriptable slots not exported + } else { + if (isScriptable && (flags & QDBusConnection::ExportScriptableInvokables) == 0) + continue; // scriptable invokables not exported + if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableInvokables) == 0) + continue; // non-scriptable invokables not exported + } + + // if we got here, this slot matched + return idx; + } + + // no slot matched + return -1; +} + +static QDBusCallDeliveryEvent * const DIRECT_DELIVERY = (QDBusCallDeliveryEvent *)1; + +QDBusCallDeliveryEvent* QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target, + QObject *object, int idx, + const QVector &metaTypes, + const QDBusMessage &msg) +{ + Q_ASSERT(object); + Q_UNUSED(object); + + int n = metaTypes.count() - 1; + if (metaTypes[n] == QDBusMetaTypeId::message()) + --n; + + if (msg.arguments().count() < n) + return 0; // too few arguments + + // check that types match + for (int i = 0; i < n; ++i) + if (metaTypes.at(i + 1) != msg.arguments().at(i).userType() && + msg.arguments().at(i).userType() != qMetaTypeId()) + return 0; // no match + + // we can deliver + // prepare for the call + if (target == object) + return DIRECT_DELIVERY; + return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes); +} + +void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook, + const QDBusMessage &msg) +{ + // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal + // that was received from D-Bus + // + // Signals are delivered to slots if the parameters match + // Slots can have less parameters than there are on the message + // Slots can optionally have one final parameter that is a QDBusMessage + // Slots receive read-only copies of the message (i.e., pass by value or by const-ref) + QDBusCallDeliveryEvent *call = prepareReply(this, hook.obj, hook.midx, hook.params, msg); + if (call == DIRECT_DELIVERY) { + // short-circuit delivery + Q_ASSERT(this == hook.obj); + deliverCall(this, 0, msg, hook.params, hook.midx); + return; + } + if (call) + postEventToThread(ActivateSignalAction, hook.obj, call); +} + +bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg) +{ + // This is called by QDBusConnectionPrivate::handleObjectCall to place a call + // to a slot on the object. + // + // The call is delivered to the first slot that matches the following conditions: + // - has the same name as the message's target member + // - ALL of the message's types are found in slot's parameter list + // - optionally has one more parameter of type QDBusMessage + // If none match, then the slot of the same name as the message target and with + // the first type of QDBusMessage is delivered. + // + // The D-Bus specification requires that all MethodCall messages be replied to, unless the + // caller specifically waived this requirement. This means that we inspect if the user slot + // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a + // QDBusMessage parameter, it cannot generate a reply. + // + // When a return message is generated, the slot's return type, if any, will be placed + // in the message's first position. If there are non-const reference parameters to the + // slot, they must appear at the end and will be placed in the subsequent message + // positions. + + static const char cachePropertyName[] = "_qdbus_slotCache"; + + if (!object) + return false; + +#ifndef QT_NO_PROPERTIES + Q_ASSERT_X(QThread::currentThread() == object->thread(), + "QDBusConnection: internal threading error", + "function called for an object that is in another thread!!"); + + QDBusSlotCache slotCache = + qvariant_cast(object->property(cachePropertyName)); + QString cacheKey = msg.member(), signature = msg.signature(); + if (!signature.isEmpty()) { + cacheKey.reserve(cacheKey.length() + 1 + signature.length()); + cacheKey += QLatin1Char('.'); + cacheKey += signature; + } + + QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(cacheKey); + while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags && + cacheIt.key() == cacheKey) + ++cacheIt; + if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey) + { + // not cached, analyze the meta object + const QMetaObject *mo = object->metaObject(); + QByteArray memberName = msg.member().toUtf8(); + + // find a slot that matches according to the rules above + QDBusSlotCache::Data slotData; + slotData.flags = flags; + slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes); + if (slotData.slotIdx == -1) { + // ### this is where we want to add the connection as an arg too + // try with no parameters, but with a QDBusMessage + slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes); + if (slotData.metaTypes.count() != 2 || + slotData.metaTypes.at(1) != QDBusMetaTypeId::message()) { + // not found + // save the negative lookup + slotData.slotIdx = -1; + slotData.metaTypes.clear(); + slotCache.hash.insert(cacheKey, slotData); + object->setProperty(cachePropertyName, QVariant::fromValue(slotCache)); + return false; + } + } + + // save to the cache + slotCache.hash.insert(cacheKey, slotData); + object->setProperty(cachePropertyName, QVariant::fromValue(slotCache)); + + // found the slot to be called + deliverCall(object, flags, msg, slotData.metaTypes, slotData.slotIdx); + return true; + } else if (cacheIt->slotIdx == -1) { + // negative cache + return false; + } else { + // use the cache + deliverCall(object, flags, msg, cacheIt->metaTypes, cacheIt->slotIdx); + return true; + } +#endif // QT_NO_PROPERTIES + return false; +} + +void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg, + const QVector &metaTypes, int slotIdx) +{ + Q_ASSERT_X(!object || QThread::currentThread() == object->thread(), + "QDBusConnection: internal threading error", + "function called for an object that is in another thread!!"); + + QVarLengthArray params; + params.reserve(metaTypes.count()); + + QVariantList auxParameters; + // let's create the parameter list + + // first one is the return type -- add it below + params.append(0); + + // add the input parameters + int i; + int pCount = qMin(msg.arguments().count(), metaTypes.count() - 1); + for (i = 1; i <= pCount; ++i) { + int id = metaTypes[i]; + if (id == QDBusMetaTypeId::message()) + break; + + const QVariant &arg = msg.arguments().at(i - 1); + if (arg.userType() == id) + // no conversion needed + params.append(const_cast(arg.constData())); + else if (arg.userType() == qMetaTypeId()) { + // convert to what the function expects + void *null = 0; + auxParameters.append(QVariant(id, null)); + + const QDBusArgument &in = + *reinterpret_cast(arg.constData()); + QVariant &out = auxParameters[auxParameters.count() - 1]; + + if (!QDBusMetaType::demarshall(in, out.userType(), out.data())) + qFatal("Internal error: demarshalling function for type '%s' (%d) failed!", + out.typeName(), out.userType()); + + params.append(const_cast(out.constData())); + } else { + qFatal("Internal error: got invalid meta type %d (%s) " + "when trying to convert to meta type %d (%s)", + arg.userType(), QMetaType::typeName(arg.userType()), + id, QMetaType::typeName(id)); + } + } + + if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message()) { + params.append(const_cast(static_cast(&msg))); + ++i; + } + + // output arguments + QVariantList outputArgs; + void *null = 0; + if (metaTypes[0] != QMetaType::Void && metaTypes[0] != QMetaType::UnknownType) { + QVariant arg(metaTypes[0], null); + outputArgs.append( arg ); + params[0] = const_cast(outputArgs.at( outputArgs.count() - 1 ).constData()); + } + for ( ; i < metaTypes.count(); ++i) { + QVariant arg(metaTypes[i], null); + outputArgs.append( arg ); + params.append(const_cast(outputArgs.at( outputArgs.count() - 1 ).constData())); + } + + // make call: + bool fail; + if (!object) { + fail = true; + } else { + // FIXME: save the old sender! + QDBusContextPrivate context(QDBusConnection(this), msg); + QDBusContextPrivate *old = QDBusContextPrivate::set(object, &context); + QDBusConnectionPrivate::setSender(this); + + QPointer ptr = object; + fail = object->qt_metacall(QMetaObject::InvokeMetaMethod, + slotIdx, params.data()) >= 0; + QDBusConnectionPrivate::setSender(0); + // the object might be deleted in the slot + if (!ptr.isNull()) + QDBusContextPrivate::set(object, old); + } + + // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent + // yet. + if (msg.isReplyRequired() && !msg.isDelayedReply()) { + if (!fail) { + // normal reply + qDBusDebug() << this << "Automatically sending reply:" << outputArgs; + send(msg.createReply(outputArgs)); + } else { + // generate internal error + qWarning("Internal error: Failed to deliver message"); + send(msg.createErrorReply(QDBusError::InternalError, + QLatin1String("Failed to deliver message"))); + } + } + + return; +} + +extern bool qDBusInitThreads(); + +QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) + : QObject(p), ref(1), capabilities(0), mode(InvalidMode), connection(0), server(0), busService(0), + watchAndTimeoutLock(QMutex::Recursive), + rootNode(QString(QLatin1Char('/'))), + anonymousAuthenticationAllowed(false) +{ + static const bool threads = q_dbus_threads_init_default(); + static const int debugging = qgetenv("QDBUS_DEBUG").toInt(); + ::isDebugging = debugging; + Q_UNUSED(threads) + Q_UNUSED(debugging) + +#ifdef QDBUS_THREAD_DEBUG + if (debugging > 1) + qdbusThreadDebug = qdbusDefaultThreadDebug; +#endif + + QDBusMetaTypeId::init(); + + rootNode.flags = 0; + + // prepopulate watchedServices: + // we know that the owner of org.freedesktop.DBus is itself + watchedServices.insert(dbusServiceString(), WatchedServiceData(dbusServiceString(), 1)); + + // prepopulate matchRefCounts: + // we know that org.freedesktop.DBus will never change owners + matchRefCounts.insert("type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.freedesktop.DBus'", 1); +} + +QDBusConnectionPrivate::~QDBusConnectionPrivate() +{ + if (thread() && thread() != QThread::currentThread()) + qWarning("QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! " + "Timer and socket errors will follow and the program will probably crash", + qPrintable(name)); + + closeConnection(); + rootNode.children.clear(); // free resources + qDeleteAll(cachedMetaObjects); + + if (server) + q_dbus_server_unref(server); + if (connection) + q_dbus_connection_unref(connection); + + connection = 0; + server = 0; +} + +void QDBusConnectionPrivate::deleteYourself() +{ + if (thread() && thread() != QThread::currentThread()) { + // last reference dropped while not in the correct thread + // ask the correct thread to delete + + // note: since we're posting an event to another thread, we + // must consider deleteLater() to take effect immediately + deleteLater(); + } else { + delete this; + } +} + +void QDBusConnectionPrivate::closeConnection() +{ + QDBusWriteLocker locker(CloseConnectionAction, this); + ConnectionMode oldMode = mode; + mode = InvalidMode; // prevent reentrancy + baseService.clear(); + + if (server) + q_dbus_server_disconnect(server); + + if (oldMode == ClientMode || oldMode == PeerMode) { + if (connection) { + q_dbus_connection_close(connection); + // send the "close" message + while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) + ; + } + } + + qDeleteAll(pendingCalls); + + qDBusDebug() << this << "Disconnected"; +} + +void QDBusConnectionPrivate::checkThread() +{ + if (!thread()) { + if (QCoreApplication::instance()) + moveToThread(QCoreApplication::instance()->thread()); + else + qWarning("The thread that had QDBusConnection('%s') has died and there is no main thread", + qPrintable(name)); + } +} + +bool QDBusConnectionPrivate::handleError(const QDBusErrorInternal &error) +{ + if (!error) + return false; // no error + + //lock.lockForWrite(); + lastError = error; + //lock.unlock(); + return true; +} + +void QDBusConnectionPrivate::timerEvent(QTimerEvent *e) +{ + { + QDBusWatchAndTimeoutLocker locker(TimerEventAction, this); + DBusTimeout *timeout = timeouts.value(e->timerId(), 0); + if (timeout) + q_dbus_timeout_handle(timeout); + } + + doDispatch(); +} + +void QDBusConnectionPrivate::customEvent(QEvent *e) +{ + Q_ASSERT(e->type() == QEvent::User); + + QDBusConnectionCallbackEvent *ev = static_cast(e); + QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), + QDBusLockerBase::BeforeDeliver, this); + switch (ev->subtype) + { + case QDBusConnectionCallbackEvent::AddTimeout: { + QDBusWatchAndTimeoutLocker locker(RealAddTimeoutAction, this); + while (!timeoutsPendingAdd.isEmpty()) { + QPair entry = timeoutsPendingAdd.takeFirst(); + qDBusRealAddTimeout(this, entry.first, entry.second); + } + break; + } + + case QDBusConnectionCallbackEvent::KillTimer: + killTimer(ev->timerId); + break; + + case QDBusConnectionCallbackEvent::AddWatch: + qDBusRealAddWatch(this, ev->watch, ev->extra, ev->fd); + break; + + case QDBusConnectionCallbackEvent::ToggleWatch: + qDBusRealToggleWatch(this, ev->watch, ev->fd); + break; + } + QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), + QDBusLockerBase::AfterDeliver, this); +} + +void QDBusConnectionPrivate::doDispatch() +{ + QDBusDispatchLocker locker(DoDispatchAction, this); + if (mode == ClientMode || mode == PeerMode) + while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; +} + +void QDBusConnectionPrivate::socketRead(int fd) +{ + QVarLengthArray pendingWatches; + + { + QDBusWatchAndTimeoutLocker locker(SocketReadAction, this); + WatcherHash::ConstIterator it = watchers.constFind(fd); + while (it != watchers.constEnd() && it.key() == fd) { + if (it->watch && it->read && it->read->isEnabled()) + pendingWatches.append(it.value().watch); + ++it; + } + } + + for (int i = 0; i < pendingWatches.size(); ++i) + if (!q_dbus_watch_handle(pendingWatches[i], DBUS_WATCH_READABLE)) + qDebug("OUT OF MEM"); + doDispatch(); +} + +void QDBusConnectionPrivate::socketWrite(int fd) +{ + QVarLengthArray pendingWatches; + + { + QDBusWatchAndTimeoutLocker locker(SocketWriteAction, this); + WatcherHash::ConstIterator it = watchers.constFind(fd); + while (it != watchers.constEnd() && it.key() == fd) { + if (it->watch && it->write && it->write->isEnabled()) + pendingWatches.append(it.value().watch); + ++it; + } + } + + for (int i = 0; i < pendingWatches.size(); ++i) + if (!q_dbus_watch_handle(pendingWatches[i], DBUS_WATCH_WRITABLE)) + qDebug("OUT OF MEM"); +} + +void QDBusConnectionPrivate::objectDestroyed(QObject *obj) +{ + QDBusWriteLocker locker(ObjectDestroyedAction, this); + huntAndDestroy(obj, rootNode); + + SignalHookHash::iterator sit = signalHooks.begin(); + while (sit != signalHooks.end()) { + if (static_cast(sit.value().obj) == obj) + sit = disconnectSignal(sit); + else + ++sit; + } + + obj->disconnect(this); +} + +void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, int signalId, + const QVariantList &args) +{ + QString interface = qDBusInterfaceFromMetaObject(mo); + + QMetaMethod mm = mo->method(signalId); + QByteArray memberName = mm.name(); + + // check if it's scriptable + bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; + bool isAdaptor = false; + for ( ; mo; mo = mo->superClass()) + if (mo == &QDBusAbstractAdaptor::staticMetaObject) { + isAdaptor = true; + break; + } + + QDBusReadLocker locker(RelaySignalAction, this); + QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/"), interface, + QLatin1String(memberName)); + QDBusMessagePrivate::setParametersValidated(message, true); + message.setArguments(args); + QDBusError error; + DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error); + if (!msg) { + qWarning("QDBusConnection: Could not emit signal %s.%s: %s", qPrintable(interface), memberName.constData(), + qPrintable(error.message())); + lastError = error; + return; + } + + //qDBusDebug() << "Emitting signal" << message; + //qDBusDebug() << "for paths:"; + q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything + huntAndEmit(connection, msg, obj, rootNode, isScriptable, isAdaptor); + q_dbus_message_unref(msg); +} + +void QDBusConnectionPrivate::serviceOwnerChangedNoLock(const QString &name, + const QString &oldOwner, const QString &newOwner) +{ + Q_UNUSED(oldOwner); +// QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this); + WatchedServicesHash::Iterator it = watchedServices.find(name); + if (it == watchedServices.end()) + return; + if (oldOwner != it->owner) + qWarning("QDBusConnection: name '%s' had owner '%s' but we thought it was '%s'", + qPrintable(name), qPrintable(oldOwner), qPrintable(it->owner)); + + qDBusDebug() << this << "Updating name" << name << "from" << oldOwner << "to" << newOwner; + it->owner = newOwner; +} + +int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName, + QVector ¶ms) +{ + int midx = obj->metaObject()->indexOfMethod(normalizedName); + if (midx == -1) + return -1; + + QString errorMsg; + int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params, errorMsg); + if ( inputCount == -1 || inputCount + 1 != params.count() ) + return -1; // failed to parse or invalid arguments or output arguments + + return midx; +} + +bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, + const QString &service, + const QString &path, const QString &interface, const QString &name, + const QStringList &argMatch, + QObject *receiver, const char *signal, int minMIdx, + bool buildSignature) +{ + QByteArray normalizedName = signal + 1; + hook.midx = findSlot(receiver, signal + 1, hook.params); + if (hook.midx == -1) { + normalizedName = QMetaObject::normalizedSignature(signal + 1); + hook.midx = findSlot(receiver, normalizedName, hook.params); + } + if (hook.midx < minMIdx) { + if (hook.midx == -1) + {} + return false; + } + + hook.service = service; + hook.path = path; + hook.obj = receiver; + hook.argumentMatch = argMatch; + + // build the D-Bus signal name and signature + // This should not happen for QDBusConnection::connect, use buildSignature here, since + // QDBusConnection::connect passes false and everything else uses true + QString mname = name; + if (buildSignature && mname.isNull()) { + normalizedName.truncate(normalizedName.indexOf('(')); + mname = QString::fromUtf8(normalizedName); + } + key = mname; + key.reserve(interface.length() + 1 + mname.length()); + key += QLatin1Char(':'); + key += interface; + + if (buildSignature) { + hook.signature.clear(); + for (int i = 1; i < hook.params.count(); ++i) + if (hook.params.at(i) != QDBusMetaTypeId::message()) + hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) ); + } + + hook.matchRule = buildMatchRule(service, path, interface, mname, argMatch, hook.signature); + return true; // connect to this signal +} + +void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::ErrorType code) +{ + if (code == QDBusError::UnknownMethod) { + QString interfaceMsg; + if (msg.interface().isEmpty()) + interfaceMsg = QLatin1String("any interface"); + else + interfaceMsg = QString::fromLatin1("interface '%1'").arg(msg.interface()); + + send(msg.createErrorReply(code, + QString::fromLatin1("No such method '%1' in %2 at object path '%3' " + "(signature '%4')") + .arg(msg.member(), interfaceMsg, msg.path(), msg.signature()))); + } else if (code == QDBusError::UnknownInterface) { + send(msg.createErrorReply(QDBusError::UnknownInterface, + QString::fromLatin1("No such interface '%1' at object path '%2'") + .arg(msg.interface(), msg.path()))); + } else if (code == QDBusError::UnknownObject) { + send(msg.createErrorReply(QDBusError::UnknownObject, + QString::fromLatin1("No such object path '%1'").arg(msg.path()))); + } +} + +bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode &node, + const QDBusMessage &msg) +{ + // object may be null + const QString interface = msg.interface(); + + if (interface.isEmpty() || interface == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) { + if (msg.member() == QLatin1String("Introspect") && msg.signature().isEmpty()) { + //qDebug() << "QDBusConnectionPrivate::activateInternalFilters introspect" << msg.d_ptr->msg; + QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node, msg.path())); + send(reply); + return true; + } + + if (!interface.isEmpty()) { + sendError(msg, QDBusError::UnknownMethod); + return true; + } + } + + if (node.obj && (interface.isEmpty() || + interface == QLatin1String(DBUS_INTERFACE_PROPERTIES))) { + //qDebug() << "QDBusConnectionPrivate::activateInternalFilters properties" << msg.d_ptr->msg; + if (msg.member() == QLatin1String("Get") && msg.signature() == QLatin1String("ss")) { + QDBusMessage reply = qDBusPropertyGet(node, msg); + send(reply); + return true; + } else if (msg.member() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) { + QDBusMessage reply = qDBusPropertySet(node, msg); + send(reply); + return true; + } else if (msg.member() == QLatin1String("GetAll") && msg.signature() == QLatin1String("s")) { + QDBusMessage reply = qDBusPropertyGetAll(node, msg); + send(reply); + return true; + } + + if (!interface.isEmpty()) { + sendError(msg, QDBusError::UnknownMethod); + return true; + } + } + + return false; +} + +void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMessage &msg, + int pathStartPos) +{ + // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot + // on the object. + // + // The call is routed through the adaptor sub-objects if we have any + + // object may be null + + if (node.flags & QDBusConnectionPrivate::VirtualObject) { + if (node.treeNode->handleMessage(msg, q(this))) { + return; + } else { + if (activateInternalFilters(node, msg)) + return; + } + } + + if (pathStartPos != msg.path().length()) { + node.flags &= ~QDBusConnection::ExportAllSignals; + node.obj = findChildObject(&node, msg.path(), pathStartPos); + if (!node.obj) { + sendError(msg, QDBusError::UnknownObject); + return; + } + } + + QDBusAdaptorConnector *connector; + if (node.flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node.obj))) { + int newflags = node.flags | QDBusConnection::ExportAllSlots; + + if (msg.interface().isEmpty()) { + // place the call in all interfaces + // let the first one that handles it to work + QDBusAdaptorConnector::AdaptorMap::ConstIterator it = + connector->adaptors.constBegin(); + QDBusAdaptorConnector::AdaptorMap::ConstIterator end = + connector->adaptors.constEnd(); + + for ( ; it != end; ++it) + if (activateCall(it->adaptor, newflags, msg)) + return; + } else { + // check if we have an interface matching the name that was asked: + QDBusAdaptorConnector::AdaptorMap::ConstIterator it; + it = std::lower_bound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), + msg.interface()); + if (it != connector->adaptors.constEnd() && msg.interface() == QLatin1String(it->interface)) { + if (!activateCall(it->adaptor, newflags, msg)) + sendError(msg, QDBusError::UnknownMethod); + return; + } + } + } + + // no adaptors matched or were exported + // try our standard filters + if (activateInternalFilters(node, msg)) + return; // internal filters have already run or an error has been sent + + // try the object itself: + if (node.flags & (QDBusConnection::ExportScriptableSlots|QDBusConnection::ExportNonScriptableSlots) || + node.flags & (QDBusConnection::ExportScriptableInvokables|QDBusConnection::ExportNonScriptableInvokables)) { + bool interfaceFound = true; + if (!msg.interface().isEmpty()) + interfaceFound = qDBusInterfaceInObject(node.obj, msg.interface()); + + if (interfaceFound) { + if (!activateCall(node.obj, node.flags, msg)) + sendError(msg, QDBusError::UnknownMethod); + return; + } + } + + // nothing matched, send an error code + if (msg.interface().isEmpty()) + sendError(msg, QDBusError::UnknownMethod); + else + sendError(msg, QDBusError::UnknownInterface); +} + +void QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg) +{ + // if the msg is external, we were called from inside doDispatch + // that means the dispatchLock mutex is locked + // must not call out to user code in that case + // + // however, if the message is internal, handleMessage was called + // directly and no lock is in place. We can therefore call out to + // user code, if necessary + ObjectTreeNode result; + int usedLength; + QThread *objThread = 0; + QSemaphore sem; + bool semWait; + + { + QDBusReadLocker locker(HandleObjectCallAction, this); + if (!findObject(&rootNode, msg.path(), usedLength, result)) { + // qDebug("Call failed: no object found at %s", qPrintable(msg.path())); + sendError(msg, QDBusError::UnknownObject); + return; + } + + if (!result.obj) { + // no object -> no threading issues + // it's either going to be an error, or an internal filter + activateObject(result, msg, usedLength); + return; + } + + objThread = result.obj->thread(); + if (!objThread) { + send(msg.createErrorReply(QDBusError::InternalError, + QString::fromLatin1("Object '%1' (at path '%2')" + " has no thread. Cannot deliver message.") + .arg(result.obj->objectName(), msg.path()))); + return; + } + + if (!QDBusMessagePrivate::isLocal(msg)) { + // external incoming message + // post it and forget + postEventToThread(HandleObjectCallPostEventAction, result.obj, + new QDBusActivateObjectEvent(QDBusConnection(this), this, result, + usedLength, msg)); + return; + } else if (objThread != QThread::currentThread()) { + // synchronize with other thread + postEventToThread(HandleObjectCallPostEventAction, result.obj, + new QDBusActivateObjectEvent(QDBusConnection(this), this, result, + usedLength, msg, &sem)); + semWait = true; + } else { + semWait = false; + } + } // release the lock + + if (semWait) + SEM_ACQUIRE(HandleObjectCallSemaphoreAction, sem); + else + activateObject(result, msg, usedLength); +} + +QDBusActivateObjectEvent::~QDBusActivateObjectEvent() +{ + if (!handled) { + // we're being destroyed without delivering + // it means the object was deleted between posting and delivering + QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection); + that->sendError(message, QDBusError::UnknownObject); + } + + // semaphore releasing happens in ~QMetaCallEvent +} + +void QDBusActivateObjectEvent::placeMetaCall(QObject *) +{ + QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection); + + QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction, + QDBusLockerBase::BeforeDeliver, that); + that->activateObject(node, message, pathStartPos); + QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction, + QDBusLockerBase::AfterDeliver, that); + + handled = true; +} + +void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg) +{ + SignalHookHash::const_iterator it = signalHooks.constFind(key); + SignalHookHash::const_iterator end = signalHooks.constEnd(); + //qDebug("looking for: %s", path.toLocal8Bit().constData()); + //qDBusDebug() << signalHooks.keys(); + for ( ; it != end && it.key() == key; ++it) { + const SignalHook &hook = it.value(); + if (!hook.service.isEmpty()) { + const QString owner = + shouldWatchService(hook.service) ? + watchedServices.value(hook.service).owner : + hook.service; + if (owner != msg.service()) + continue; + } + if (!hook.path.isEmpty() && hook.path != msg.path()) + continue; + if (!hook.signature.isEmpty() && hook.signature != msg.signature()) + continue; + if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty()) + continue; + if (!hook.argumentMatch.isEmpty()) { + const QVariantList arguments = msg.arguments(); + if (hook.argumentMatch.size() > arguments.size()) + continue; + + bool matched = true; + for (int i = 0; i < hook.argumentMatch.size(); ++i) { + const QString ¶m = hook.argumentMatch.at(i); + if (param.isNull()) + continue; // don't try to match against this + if (param == arguments.at(i).toString()) + continue; // matched + matched = false; + break; + } + if (!matched) + continue; + } + + activateSignal(hook, msg); + } +} + +void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) +{ + // We call handlesignal(QString, QDBusMessage) three times: + // one with member:interface + // one with member: + // one with :interface + // This allows us to match signals with wildcards on member or interface + // (but not both) + + QString key = msg.member(); + key.reserve(key.length() + 1 + msg.interface().length()); + key += QLatin1Char(':'); + key += msg.interface(); + + QDBusReadLocker locker(HandleSignalAction, this); + handleSignal(key, msg); // one try + + key.truncate(msg.member().length() + 1); // keep the ':' + handleSignal(key, msg); // second try + + key = QLatin1Char(':'); + key += msg.interface(); + handleSignal(key, msg); // third try +} + +static dbus_int32_t server_slot = -1; + +void QDBusConnectionPrivate::setServer(DBusServer *s, const QDBusErrorInternal &error) +{ + mode = ServerMode; + if (!s) { + handleError(error); + return; + } + + server = s; + + dbus_bool_t data_allocated = q_dbus_server_allocate_data_slot(&server_slot); + if (data_allocated && server_slot < 0) + return; + + dbus_bool_t watch_functions_set = q_dbus_server_set_watch_functions(server, + qDBusAddWatch, + qDBusRemoveWatch, + qDBusToggleWatch, + this, 0); + //qDebug() << "watch_functions_set" << watch_functions_set; + Q_UNUSED(watch_functions_set); + + dbus_bool_t time_functions_set = q_dbus_server_set_timeout_functions(server, + qDBusAddTimeout, + qDBusRemoveTimeout, + qDBusToggleTimeout, + this, 0); + //qDebug() << "time_functions_set" << time_functions_set; + Q_UNUSED(time_functions_set); + + q_dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0); + + dbus_bool_t data_set = q_dbus_server_set_data(server, server_slot, this, 0); + //qDebug() << "data_set" << data_set; + Q_UNUSED(data_set); +} + +void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal &error) +{ + mode = PeerMode; + if (!c) { + handleError(error); + return; + } + + connection = c; + + q_dbus_connection_set_exit_on_disconnect(connection, false); + q_dbus_connection_set_watch_functions(connection, + qDBusAddWatch, + qDBusRemoveWatch, + qDBusToggleWatch, + this, 0); + q_dbus_connection_set_timeout_functions(connection, + qDBusAddTimeout, + qDBusRemoveTimeout, + qDBusToggleTimeout, + this, 0); + q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, 0); + q_dbus_connection_add_filter(connection, + qDBusSignalFilter, + this, 0); + + QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); +} + +static QDBusConnection::ConnectionCapabilities connectionCapabilies(DBusConnection *connection) +{ + QDBusConnection::ConnectionCapabilities result = 0; + typedef dbus_bool_t (*can_send_type_t)(DBusConnection *, int); + static can_send_type_t can_send_type = 0; + +#if defined(QT_LINKED_LIBDBUS) +# if DBUS_VERSION-0 >= 0x010400 + can_send_type = dbus_connection_can_send_type; +# endif +#else + // run-time check if the next functions are available + can_send_type = (can_send_type_t)qdbus_resolve_conditionally("dbus_connection_can_send_type"); +#endif + +#ifndef DBUS_TYPE_UNIX_FD +# define DBUS_TYPE_UNIX_FD int('h') +#endif + if (can_send_type && can_send_type(connection, DBUS_TYPE_UNIX_FD)) + result |= QDBusConnection::UnixFileDescriptorPassing; + + return result; +} + +void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusErrorInternal &error) +{ + mode = ClientMode; + if (!dbc) { + handleError(error); + return; + } + + connection = dbc; + + const char *service = q_dbus_bus_get_unique_name(connection); + Q_ASSERT(service); + baseService = QString::fromUtf8(service); + capabilities = connectionCapabilies(connection); + + q_dbus_connection_set_exit_on_disconnect(connection, false); + q_dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch, + qDBusToggleWatch, this, 0); + q_dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout, + qDBusToggleTimeout, this, 0); + q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, 0); + q_dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0); + + // Initialize the hooks for the NameAcquired and NameLost signals + // we don't use connectSignal here because we don't need the rules to be sent to the bus + // the bus will always send us these two signals + SignalHook hook; + hook.service = dbusServiceString(); + hook.path.clear(); // no matching + hook.obj = this; + hook.params << QMetaType::Void << QVariant::String; // both functions take a QString as parameter and return void + + hook.midx = staticMetaObject.indexOfSlot("registerServiceNoLock(QString)"); + Q_ASSERT(hook.midx != -1); + signalHooks.insert(QLatin1String("NameAcquired:" DBUS_INTERFACE_DBUS), hook); + + hook.midx = staticMetaObject.indexOfSlot("unregisterServiceNoLock(QString)"); + Q_ASSERT(hook.midx != -1); + signalHooks.insert(QLatin1String("NameLost:" DBUS_INTERFACE_DBUS), hook); + + qDBusDebug() << this << ": connected successfully"; + + // schedule a dispatch: + QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); +} + +extern "C"{ +static void qDBusResultReceived(DBusPendingCall *pending, void *user_data) +{ + QDBusPendingCallPrivate *call = reinterpret_cast(user_data); + Q_ASSERT(call->pending == pending); + Q_UNUSED(pending); + QDBusConnectionPrivate::processFinishedCall(call); +} +} + +void QDBusConnectionPrivate::waitForFinished(QDBusPendingCallPrivate *pcall) +{ + Q_ASSERT(pcall->pending); + //Q_ASSERT(pcall->mutex.isLocked()); // there's no such function + + if (pcall->waitingForFinished) { + // another thread is already waiting + pcall->waitForFinishedCondition.wait(&pcall->mutex); + } else { + pcall->waitingForFinished = true; + pcall->mutex.unlock(); + + { + QDBusDispatchLocker locker(PendingCallBlockAction, this); + q_dbus_pending_call_block(pcall->pending); + // QDBusConnectionPrivate::processFinishedCall() is called automatically + } + pcall->mutex.lock(); + + if (pcall->pending) { + q_dbus_pending_call_unref(pcall->pending); + pcall->pending = 0; + } + + pcall->waitForFinishedCondition.wakeAll(); + } +} + +void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call) +{ + QDBusConnectionPrivate *connection = const_cast(call->connection); + + QMutexLocker locker(&call->mutex); + + connection->pendingCalls.removeOne(call); + + QDBusMessage &msg = call->replyMessage; + if (call->pending) { + // decode the message + DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending); + msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->capabilities); + q_dbus_message_unref(reply); + } + qDBusDebug() << connection << "got message reply (async):" << msg; + + // Check if the reply has the expected signature + call->checkReceivedSignature(); + + if (!call->receiver.isNull() && call->methodIdx != -1 && msg.type() == QDBusMessage::ReplyMessage) { + // Deliver the return values of a remote function call. + // + // There is only one connection and it is specified by idx + // The slot must have the same parameter types that the message does + // The slot may have less parameters than the message + // The slot may optionally have one final parameter that is QDBusMessage + // The slot receives read-only copies of the message (i.e., pass by value or by const-ref) + + QDBusCallDeliveryEvent *e = prepareReply(connection, call->receiver, call->methodIdx, + call->metaTypes, msg); + if (e) + connection->postEventToThread(MessageResultReceivedAction, call->receiver, e); + else + qDBusDebug() << "Deliver failed!"; + } + + if (call->pending && !call->waitingForFinished) { + q_dbus_pending_call_unref(call->pending); + call->pending = 0; + } + + locker.unlock(); + + // Are there any watchers? + if (call->watcherHelper) + call->watcherHelper->emitSignals(msg, call->sentMessage); + + if (msg.type() == QDBusMessage::ErrorMessage) + emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage); + + if (!call->ref.deref()) + delete call; +} + +int QDBusConnectionPrivate::send(const QDBusMessage& message) +{ + if (QDBusMessagePrivate::isLocal(message)) + return -1; // don't send; the reply will be retrieved by the caller + // through the d_ptr->localReply link + + QDBusError error; + DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error); + if (!msg) { + if (message.type() == QDBusMessage::MethodCallMessage) + qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member()), + qPrintable(error.message())); + else if (message.type() == QDBusMessage::SignalMessage) + qWarning("QDBusConnection: error: could not send signal path \"%s\" interface \"%s\" member \"%s\": %s", + qPrintable(message.path()), qPrintable(message.interface()), + qPrintable(message.member()), + qPrintable(error.message())); + else + qWarning("QDBusConnection: error: could not send %s message to service \"%s\": %s", + message.type() == QDBusMessage::ReplyMessage ? "reply" : + message.type() == QDBusMessage::ErrorMessage ? "error" : + "invalid", qPrintable(message.service()), + qPrintable(error.message())); + lastError = error; + return 0; + } + + q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything + + qDBusDebug() << this << "sending message (no reply):" << message; + checkThread(); + bool isOk = q_dbus_connection_send(connection, msg, 0); + int serial = 0; + if (isOk) + serial = q_dbus_message_get_serial(msg); + + q_dbus_message_unref(msg); + return serial; +} + +QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, + int sendMode, int timeout) +{ + checkThread(); + if ((sendMode == QDBus::BlockWithGui || sendMode == QDBus::Block) + && isServiceRegisteredByThread(message.service())) + // special case for synchronous local calls + return sendWithReplyLocal(message); + + if (!QCoreApplication::instance() || sendMode == QDBus::Block) { + QDBusError err; + DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &err); + if (!msg) { + qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member()), + qPrintable(err.message())); + lastError = err; + return QDBusMessage::createError(err); + } + + qDBusDebug() << this << "sending message (blocking):" << message; + QDBusErrorInternal error; + DBusMessage *reply = q_dbus_connection_send_with_reply_and_block(connection, msg, timeout, error); + + q_dbus_message_unref(msg); + + if (!!error) { + lastError = err = error; + return QDBusMessage::createError(err); + } + + QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(reply, capabilities); + q_dbus_message_unref(reply); + qDBusDebug() << this << "got message reply (blocking):" << amsg; + + return amsg; + } else { // use the event loop + QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, 0, 0, 0, timeout); + Q_ASSERT(pcall); + + if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) { + pcall->watcherHelper = new QDBusPendingCallWatcherHelper; + QEventLoop loop; + loop.connect(pcall->watcherHelper, SIGNAL(reply(QDBusMessage)), SLOT(quit())); + loop.connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), SLOT(quit())); + + // enter the event loop and wait for a reply + loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); + } + + QDBusMessage reply = pcall->replyMessage; + lastError = QDBusError(reply); // set or clear error + + bool r = pcall->ref.deref(); + Q_ASSERT(!r); + Q_UNUSED(r); + + delete pcall; + return reply; + } +} + +QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &message) +{ + qDBusDebug() << this << "sending message via local-loop:" << message; + + QDBusMessage localCallMsg = QDBusMessagePrivate::makeLocal(*this, message); + bool handled = handleMessage(localCallMsg); + + if (!handled) { + QString interface = message.interface(); + if (interface.isEmpty()) + interface = QLatin1String(""); + return QDBusMessage::createError(QDBusError::InternalError, + QString::fromLatin1("Internal error trying to call %1.%2 at %3 (signature '%4'") + .arg(interface, message.member(), + message.path(), message.signature())); + } + + // if the message was handled, there might be a reply + QDBusMessage localReplyMsg = QDBusMessagePrivate::makeLocalReply(*this, localCallMsg); + if (localReplyMsg.type() == QDBusMessage::InvalidMessage) { + qWarning("QDBusConnection: cannot call local method '%s' at object %s (with signature '%s') " + "on blocking mode", qPrintable(message.member()), qPrintable(message.path()), + qPrintable(message.signature())); + return QDBusMessage::createError( + QDBusError(QDBusError::InternalError, + QLatin1String("local-loop message cannot have delayed replies"))); + } + + // there is a reply + qDBusDebug() << this << "got message via local-loop:" << localReplyMsg; + return localReplyMsg; +} + +QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, + QObject *receiver, const char *returnMethod, + const char *errorMethod, int timeout) +{ + if (isServiceRegisteredByThread(message.service())) { + // special case for local calls + QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this); + pcall->replyMessage = sendWithReplyLocal(message); + if (receiver && returnMethod) + pcall->setReplyCallback(receiver, returnMethod); + + if (errorMethod) { + pcall->watcherHelper = new QDBusPendingCallWatcherHelper; + connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod, + Qt::QueuedConnection); + pcall->watcherHelper->moveToThread(thread()); + } + + if ((receiver && returnMethod) || errorMethod) { + // no one waiting, will delete pcall in processFinishedCall() + pcall->ref.store(1); + } else { + // set double ref to prevent race between processFinishedCall() and ref counting + // by QDBusPendingCall::QExplicitlySharedDataPointer + pcall->ref.store(2); + } + processFinishedCall(pcall); + return pcall; + } + + checkThread(); + QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this); + if (receiver && returnMethod) + pcall->setReplyCallback(receiver, returnMethod); + + if (errorMethod) { + pcall->watcherHelper = new QDBusPendingCallWatcherHelper; + connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod, + Qt::QueuedConnection); + pcall->watcherHelper->moveToThread(thread()); + } + + if ((receiver && returnMethod) || errorMethod) { + // no one waiting, will delete pcall in processFinishedCall() + pcall->ref.store(1); + } else { + // set double ref to prevent race between processFinishedCall() and ref counting + // by QDBusPendingCall::QExplicitlySharedDataPointer + pcall->ref.store(2); + } + + QDBusError error; + DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error); + if (!msg) { + qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member()), + qPrintable(error.message())); + pcall->replyMessage = QDBusMessage::createError(error); + lastError = error; + processFinishedCall(pcall); + return pcall; + } + + qDBusDebug() << this << "sending message (async):" << message; + DBusPendingCall *pending = 0; + + QDBusDispatchLocker locker(SendWithReplyAsyncAction, this); + if (q_dbus_connection_send_with_reply(connection, msg, &pending, timeout)) { + if (pending) { + q_dbus_message_unref(msg); + + pcall->pending = pending; + q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0); + + // DBus won't notify us when a peer disconnects so we need to track these ourselves + if (mode == QDBusConnectionPrivate::PeerMode) + pendingCalls.append(pcall); + + return pcall; + } else { + // we're probably disconnected at this point + lastError = error = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to server")); + } + } else { + lastError = error = QDBusError(QDBusError::NoMemory, QLatin1String("Out of memory")); + } + + q_dbus_message_unref(msg); + pcall->replyMessage = QDBusMessage::createError(error); + processFinishedCall(pcall); + return pcall; +} + +bool QDBusConnectionPrivate::connectSignal(const QString &service, + const QString &path, const QString &interface, const QString &name, + const QStringList &argumentMatch, const QString &signature, + QObject *receiver, const char *slot) +{ + // check the slot + QDBusConnectionPrivate::SignalHook hook; + QString key; + QString name2 = name; + if (name2.isNull()) + name2.detach(); + + hook.signature = signature; + if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false)) + return false; // don't connect + + // avoid duplicating: + QDBusConnectionPrivate::SignalHookHash::ConstIterator it = signalHooks.constFind(key); + QDBusConnectionPrivate::SignalHookHash::ConstIterator end = signalHooks.constEnd(); + for ( ; it != end && it.key() == key; ++it) { + const QDBusConnectionPrivate::SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx && + entry.argumentMatch == hook.argumentMatch) { + // no need to compare the parameters if it's the same slot + return true; // already there + } + } + + connectSignal(key, hook); + return true; +} + +void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook) +{ + signalHooks.insertMulti(key, hook); + connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), + Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); + + MatchRefCountHash::iterator it = matchRefCounts.find(hook.matchRule); + + if (it != matchRefCounts.end()) { // Match already present + it.value() = it.value() + 1; + return; + } + + matchRefCounts.insert(hook.matchRule, 1); + + if (connection) { + if (mode != QDBusConnectionPrivate::PeerMode) { + qDBusDebug("Adding rule: %s", hook.matchRule.constData()); + q_dbus_bus_add_match(connection, hook.matchRule, NULL); + + // Successfully connected the signal + // Do we need to watch for this name? + if (shouldWatchService(hook.service)) { + WatchedServicesHash::mapped_type &data = watchedServices[hook.service]; + if (++data.refcount == 1) { + // we need to watch for this service changing + connectSignal(dbusServiceString(), QString(), dbusInterfaceString(), + QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(), + this, SLOT(serviceOwnerChangedNoLock(QString,QString,QString))); + data.owner = getNameOwnerNoCache(hook.service); + qDBusDebug() << this << "Watching service" << hook.service << "for owner changes (current owner:" + << data.owner << ")"; + } + } + } + } +} + +bool QDBusConnectionPrivate::disconnectSignal(const QString &service, + const QString &path, const QString &interface, const QString &name, + const QStringList &argumentMatch, const QString &signature, + QObject *receiver, const char *slot) +{ + // check the slot + QDBusConnectionPrivate::SignalHook hook; + QString key; + QString name2 = name; + if (name2.isNull()) + name2.detach(); + + hook.signature = signature; + if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false)) + return false; // don't disconnect + + // avoid duplicating: + QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key); + QDBusConnectionPrivate::SignalHookHash::Iterator end = signalHooks.end(); + for ( ; it != end && it.key() == key; ++it) { + const QDBusConnectionPrivate::SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx && + entry.argumentMatch == hook.argumentMatch) { + // no need to compare the parameters if it's the same slot + disconnectSignal(it); + return true; // it was there + } + } + + // the slot was not found + return false; +} + +QDBusConnectionPrivate::SignalHookHash::Iterator +QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) +{ + const SignalHook &hook = it.value(); + + bool erase = false; + MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule); + if (i == matchRefCounts.end()) { + qWarning("QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!"); + } else { + if (i.value() == 1) { + erase = true; + matchRefCounts.erase(i); + } + else { + i.value() = i.value() - 1; + } + } + + // we don't care about errors here + if (connection && erase) { + if (mode != QDBusConnectionPrivate::PeerMode) { + qDBusDebug("Removing rule: %s", hook.matchRule.constData()); + q_dbus_bus_remove_match(connection, hook.matchRule, NULL); + + // Successfully disconnected the signal + // Were we watching for this name? + WatchedServicesHash::Iterator sit = watchedServices.find(hook.service); + if (sit != watchedServices.end()) { + if (--sit.value().refcount == 0) { + watchedServices.erase(sit); + disconnectSignal(dbusServiceString(), QString(), dbusInterfaceString(), + QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(), + this, SLOT(serviceOwnerChangedNoLock(QString,QString,QString))); + } + } + } + + } + + return signalHooks.erase(it); +} + +void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node) +{ + connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), + Qt::DirectConnection); + + if (node->flags & (QDBusConnection::ExportAdaptors + | QDBusConnection::ExportScriptableSignals + | QDBusConnection::ExportNonScriptableSignals)) { + QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj); + + if (node->flags & (QDBusConnection::ExportScriptableSignals + | QDBusConnection::ExportNonScriptableSignals)) { + connector->disconnectAllSignals(node->obj); + connector->connectAllSignals(node->obj); + } + + // disconnect and reconnect to avoid duplicates + connector->disconnect(SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), + this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList))); + connect(connector, SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), + this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), + Qt::DirectConnection); + } +} + +void QDBusConnectionPrivate::unregisterObject(const QString &path, QDBusConnection::UnregisterMode mode) +{ + QDBusConnectionPrivate::ObjectTreeNode *node = &rootNode; + QStringList pathComponents; + int i; + if (path == QLatin1String("/")) { + i = 0; + } else { + pathComponents = path.split(QLatin1Char('/')); + i = 1; + } + + huntAndUnregister(pathComponents, i, mode, node); +} + +void QDBusConnectionPrivate::connectRelay(const QString &service, + const QString &path, const QString &interface, + QDBusAbstractInterface *receiver, + const QMetaMethod &signal) +{ + // this function is called by QDBusAbstractInterface when one of its signals is connected + // we set up a relay from D-Bus into it + SignalHook hook; + QString key; + + QByteArray sig; + sig.append(QSIGNAL_CODE + '0'); + sig.append(signal.methodSignature()); + if (!prepareHook(hook, key, service, path, interface, QString(), QStringList(), receiver, sig, + QDBusAbstractInterface::staticMetaObject.methodCount(), true)) + return; // don't connect + + // add it to our list: + QDBusWriteLocker locker(ConnectRelayAction, this); + SignalHookHash::ConstIterator it = signalHooks.constFind(key); + SignalHookHash::ConstIterator end = signalHooks.constEnd(); + for ( ; it != end && it.key() == key; ++it) { + const SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) + return; // already there, no need to re-add + } + + connectSignal(key, hook); +} + +void QDBusConnectionPrivate::disconnectRelay(const QString &service, + const QString &path, const QString &interface, + QDBusAbstractInterface *receiver, + const QMetaMethod &signal) +{ + // this function is called by QDBusAbstractInterface when one of its signals is disconnected + // we remove relay from D-Bus into it + SignalHook hook; + QString key; + + QByteArray sig; + sig.append(QSIGNAL_CODE + '0'); + sig.append(signal.methodSignature()); + if (!prepareHook(hook, key, service, path, interface, QString(), QStringList(), receiver, sig, + QDBusAbstractInterface::staticMetaObject.methodCount(), true)) + return; // don't connect + + // remove it from our list: + QDBusWriteLocker locker(DisconnectRelayAction, this); + SignalHookHash::Iterator it = signalHooks.find(key); + SignalHookHash::Iterator end = signalHooks.end(); + for ( ; it != end && it.key() == key; ++it) { + const SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) { + // found it + disconnectSignal(it); + return; + } + } +} + +QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName) +{ + if (QDBusUtil::isValidUniqueConnectionName(serviceName)) + return serviceName; + if (!connection) + return QString(); + + { + // acquire a read lock for the cache + QReadLocker locker(&lock); + WatchedServicesHash::ConstIterator it = watchedServices.constFind(serviceName); + if (it != watchedServices.constEnd()) + return it->owner; + } + + // not cached + return getNameOwnerNoCache(serviceName); +} + +QString QDBusConnectionPrivate::getNameOwnerNoCache(const QString &serviceName) +{ + QDBusMessage msg = QDBusMessage::createMethodCall(dbusServiceString(), + QLatin1String(DBUS_PATH_DBUS), dbusInterfaceString(), + QLatin1String("GetNameOwner")); + QDBusMessagePrivate::setParametersValidated(msg, true); + msg << serviceName; + QDBusMessage reply = sendWithReply(msg, QDBus::Block); + if (reply.type() == QDBusMessage::ReplyMessage) + return reply.arguments().at(0).toString(); + return QString(); +} + +QDBusMetaObject * +QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path, + const QString &interface, QDBusError &error) +{ + // service must be a unique connection name + if (!interface.isEmpty()) { + QDBusReadLocker locker(FindMetaObject1Action, this); + QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0); + if (mo) + return mo; + } + + // introspect the target object + QDBusMessage msg = QDBusMessage::createMethodCall(service, path, + QLatin1String(DBUS_INTERFACE_INTROSPECTABLE), + QLatin1String("Introspect")); + QDBusMessagePrivate::setParametersValidated(msg, true); + + QDBusMessage reply = sendWithReply(msg, QDBus::Block); + + // it doesn't exist yet, we have to create it + QDBusWriteLocker locker(FindMetaObject2Action, this); + QDBusMetaObject *mo = 0; + if (!interface.isEmpty()) + mo = cachedMetaObjects.value(interface, 0); + if (mo) + // maybe it got created when we switched from read to write lock + return mo; + + QString xml; + if (reply.type() == QDBusMessage::ReplyMessage) { + if (reply.signature() == QLatin1String("s")) + // fetch the XML description + xml = reply.arguments().at(0).toString(); + } else { + error = QDBusError(reply); + lastError = error; + if (reply.type() != QDBusMessage::ErrorMessage || error.type() != QDBusError::UnknownMethod) + return 0; // error + } + + // release the lock and return + QDBusMetaObject *result = QDBusMetaObject::createMetaObject(interface, xml, + cachedMetaObjects, error); + lastError = error; + return result; +} + +void QDBusConnectionPrivate::registerService(const QString &serviceName) +{ + QDBusWriteLocker locker(RegisterServiceAction, this); + registerServiceNoLock(serviceName); +} + +void QDBusConnectionPrivate::registerServiceNoLock(const QString &serviceName) +{ + serviceNames.append(serviceName); +} + +void QDBusConnectionPrivate::unregisterService(const QString &serviceName) +{ + QDBusWriteLocker locker(UnregisterServiceAction, this); + unregisterServiceNoLock(serviceName); +} + +void QDBusConnectionPrivate::unregisterServiceNoLock(const QString &serviceName) +{ + serviceNames.removeAll(serviceName); +} + +bool QDBusConnectionPrivate::isServiceRegisteredByThread(const QString &serviceName) +{ + if (!serviceName.isEmpty() && serviceName == baseService) + return true; + if (serviceName == dbusServiceString()) + return false; + + QDBusReadLocker locker(UnregisterServiceAction, this); + return serviceNames.contains(serviceName); +} + +void QDBusConnectionPrivate::postEventToThread(int action, QObject *object, QEvent *ev) +{ + QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::BeforePost, this); + QCoreApplication::postEvent(object, ev); + QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterPost, this); +} + +QT_END_NAMESPACE + +#endif // QT_NO_DBUS diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/gui/opengl/qopenglfunctions.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/gui/opengl/qopenglfunctions.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/gui/opengl/qopenglfunctions.cpp 2014-06-19 12:08:06.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/gui/opengl/qopenglfunctions.cpp 2014-08-13 04:35:08.402435904 +0200 @@ -283,46 +283,48 @@ QSurfaceFormat format = QOpenGLContext::currentContext()->format(); QOpenGLExtensionMatcher extensions; - // Recognize features by extension name. - if (extensions.match("GL_ARB_multitexture")) - features |= QOpenGLFunctions::Multitexture; - if (extensions.match("GL_ARB_shader_objects")) - features |= QOpenGLFunctions::Shaders; - if (extensions.match("GL_EXT_framebuffer_object") || - extensions.match("GL_ARB_framebuffer_object")) - features |= QOpenGLFunctions::Framebuffers; - if (extensions.match("GL_EXT_blend_color")) - features |= QOpenGLFunctions::BlendColor; - if (extensions.match("GL_EXT_blend_equation_separate")) - features |= QOpenGLFunctions::BlendEquationSeparate; - if (extensions.match("GL_EXT_blend_func_separate")) - features |= QOpenGLFunctions::BlendFuncSeparate; - if (extensions.match("GL_EXT_blend_subtract")) - features |= QOpenGLFunctions::BlendSubtract; - if (extensions.match("GL_ARB_texture_compression")) - features |= QOpenGLFunctions::CompressedTextures; - if (extensions.match("GL_ARB_multisample")) - features |= QOpenGLFunctions::Multisample; - if (extensions.match("GL_ARB_texture_non_power_of_two")) - features |= QOpenGLFunctions::NPOTTextures | - QOpenGLFunctions::NPOTTextureRepeat; - - // assume version 2.0 or higher - features |= QOpenGLFunctions::BlendColor | - QOpenGLFunctions::BlendEquation | - QOpenGLFunctions::Multitexture | - QOpenGLFunctions::CompressedTextures | - QOpenGLFunctions::Multisample | - QOpenGLFunctions::BlendFuncSeparate | - QOpenGLFunctions::Buffers | - QOpenGLFunctions::Shaders | - QOpenGLFunctions::StencilSeparate | - QOpenGLFunctions::BlendEquationSeparate | - QOpenGLFunctions::NPOTTextures | - QOpenGLFunctions::NPOTTextureRepeat; - if (format.majorVersion() >= 3) features |= QOpenGLFunctions::Framebuffers; + else if (extensions.match("GL_EXT_framebuffer_object") || + extensions.match("GL_ARB_framebuffer_object")) + features |= QOpenGLFunctions::Framebuffers; + + if (format.majorVersion() >= 2) { + features |= QOpenGLFunctions::BlendColor | + QOpenGLFunctions::BlendEquation | + QOpenGLFunctions::BlendSubtract | + QOpenGLFunctions::Multitexture | + QOpenGLFunctions::CompressedTextures | + QOpenGLFunctions::Multisample | + QOpenGLFunctions::BlendFuncSeparate | + QOpenGLFunctions::Buffers | + QOpenGLFunctions::Shaders | + QOpenGLFunctions::StencilSeparate | + QOpenGLFunctions::BlendEquationSeparate | + QOpenGLFunctions::NPOTTextures | + QOpenGLFunctions::NPOTTextureRepeat; + } else { + // Recognize features by extension name. + if (extensions.match("GL_ARB_multitexture")) + features |= QOpenGLFunctions::Multitexture; + if (extensions.match("GL_ARB_shader_objects")) + features |= QOpenGLFunctions::Shaders; + if (extensions.match("GL_EXT_blend_color")) + features |= QOpenGLFunctions::BlendColor; + if (extensions.match("GL_EXT_blend_equation_separate")) + features |= QOpenGLFunctions::BlendEquationSeparate; + if (extensions.match("GL_EXT_blend_subtract")) + features |= QOpenGLFunctions::BlendSubtract; + if (extensions.match("GL_EXT_blend_func_separate")) + features |= QOpenGLFunctions::BlendFuncSeparate; + if (extensions.match("GL_ARB_texture_compression")) + features |= QOpenGLFunctions::CompressedTextures; + if (extensions.match("GL_ARB_multisample")) + features |= QOpenGLFunctions::Multisample; + if (extensions.match("GL_ARB_texture_non_power_of_two")) + features |= QOpenGLFunctions::NPOTTextures | + QOpenGLFunctions::NPOTTextureRepeat; + } const QPair version = format.version(); if (version < qMakePair(3, 0) diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/gui/text/qfontengine_ft.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/gui/text/qfontengine_ft.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/gui/text/qfontengine_ft.cpp 2014-06-19 12:08:06.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/gui/text/qfontengine_ft.cpp 2014-08-13 04:35:08.422435865 +0200 @@ -70,7 +70,7 @@ #include FT_CONFIG_OPTIONS_H #endif -#if defined(FT_LCD_FILTER_H) && defined(FT_CONFIG_OPTION_SUBPIXEL_RENDERING) +#if defined(FT_LCD_FILTER_H) #define QT_USE_FREETYPE_LCDFILTER #endif diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/gui/text/qfontengine_ft.cpp.orig qt-everywhere-opensource-src-5.3.1.new/qtbase/src/gui/text/qfontengine_ft.cpp.orig --- qt-everywhere-opensource-src-5.3.1/qtbase/src/gui/text/qfontengine_ft.cpp.orig 1970-01-01 01:00:00.000000000 +0100 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/gui/text/qfontengine_ft.cpp.orig 2014-06-19 12:08:06.000000000 +0200 @@ -0,0 +1,2158 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdir.h" +#include "qmetatype.h" +#include "qtextstream.h" +#include "qvariant.h" +#include "qfontengine_ft_p.h" +#include "private/qimage_p.h" +#include + +#ifndef QT_NO_FREETYPE + +#include "qfile.h" +#include "qfileinfo.h" +#include "qthreadstorage.h" +#include + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_SYNTHESIS_H +#include FT_TRUETYPE_TABLES_H +#include FT_TYPE1_TABLES_H +#include FT_GLYPH_H + +#if defined(FT_LCD_FILTER_H) +#include FT_LCD_FILTER_H +#endif + +#if defined(FT_CONFIG_OPTIONS_H) +#include FT_CONFIG_OPTIONS_H +#endif + +#if defined(FT_LCD_FILTER_H) && defined(FT_CONFIG_OPTION_SUBPIXEL_RENDERING) +#define QT_USE_FREETYPE_LCDFILTER +#endif + +#ifdef QT_LINUXBASE +#include FT_ERRORS_H +#endif + +#if !defined(QT_MAX_CACHED_GLYPH_SIZE) +# define QT_MAX_CACHED_GLYPH_SIZE 64 +#endif + +QT_BEGIN_NAMESPACE + +/* + * Freetype 2.1.7 and earlier used width/height + * for matching sizes in the BDF and PCF loaders. + * This has been fixed for 2.1.8. + */ +#if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105 +#define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem) +#define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem) +#else +#define X_SIZE(face,i) ((face)->available_sizes[i].width << 6) +#define Y_SIZE(face,i) ((face)->available_sizes[i].height << 6) +#endif + +/* FreeType 2.1.10 starts to provide FT_GlyphSlot_Embolden */ +#if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20110 +#define Q_FT_GLYPHSLOT_EMBOLDEN(slot) FT_GlyphSlot_Embolden(slot) +#else +#define Q_FT_GLYPHSLOT_EMBOLDEN(slot) +#endif + +/* FreeType 2.1.10 starts to provide FT_GlyphSlot_Oblique */ +#if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20110 +#define Q_HAS_FT_GLYPHSLOT_OBLIQUE +#define Q_FT_GLYPHSLOT_OBLIQUE(slot) FT_GlyphSlot_Oblique(slot) +#else +#define Q_FT_GLYPHSLOT_OBLIQUE(slot) +#endif + +#define FLOOR(x) ((x) & -64) +#define CEIL(x) (((x)+63) & -64) +#define TRUNC(x) ((x) >> 6) +#define ROUND(x) (((x)+32) & -64) + +static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length) +{ + FT_Face face = (FT_Face)user_data; + + bool result = false; + if (FT_IS_SFNT(face)) { + FT_ULong len = *length; + result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok; + *length = len; + Q_ASSERT(!result || int(*length) > 0); + } + + return result; +} + + +// -------------------------- Freetype support ------------------------------ + +class QtFreetypeData +{ +public: + QtFreetypeData() + : library(0) + { } + ~QtFreetypeData(); + + FT_Library library; + QHash faces; +}; + +QtFreetypeData::~QtFreetypeData() +{ + for (QHash::ConstIterator iter = faces.begin(); iter != faces.end(); ++iter) + iter.value()->cleanup(); + faces.clear(); + FT_Done_FreeType(library); + library = 0; +} + +#ifdef QT_NO_THREAD +Q_GLOBAL_STATIC(QtFreetypeData, theFreetypeData) + +QtFreetypeData *qt_getFreetypeData() +{ + return theFreetypeData(); +} +#else +Q_GLOBAL_STATIC(QThreadStorage, theFreetypeData) + +QtFreetypeData *qt_getFreetypeData() +{ + QtFreetypeData *&freetypeData = theFreetypeData()->localData(); + if (!freetypeData) + freetypeData = new QtFreetypeData; + return freetypeData; +} +#endif + +FT_Library qt_getFreetype() +{ + QtFreetypeData *freetypeData = qt_getFreetypeData(); + if (!freetypeData->library) + FT_Init_FreeType(&freetypeData->library); + return freetypeData->library; +} + +int QFreetypeFace::fsType() const +{ + int fsType = 0; + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); + if (os2) + fsType = os2->fsType; + return fsType; +} + +int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) +{ + if (int error = FT_Load_Glyph(face, glyph, flags)) + return error; + + if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + return Err_Invalid_SubTable; + + *nPoints = face->glyph->outline.n_points; + if (!(*nPoints)) + return Err_Ok; + + if (point > *nPoints) + return Err_Invalid_SubTable; + + *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x); + *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y); + + return Err_Ok; +} + +extern QByteArray qt_fontdata_from_index(int); + +/* + * One font file can contain more than one font (bold/italic for example) + * find the right one and return it. + * + * Returns the freetype face or 0 in case of an empty file or any other problems + * (like not being able to open the file) + */ +QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, + const QByteArray &fontData) +{ + if (face_id.filename.isEmpty() && fontData.isEmpty()) + return 0; + + QtFreetypeData *freetypeData = qt_getFreetypeData(); + if (!freetypeData->library) + FT_Init_FreeType(&freetypeData->library); + + QFreetypeFace *freetype = freetypeData->faces.value(face_id, 0); + if (freetype) { + freetype->ref.ref(); + } else { + QScopedPointer newFreetype(new QFreetypeFace); + FT_Face face; + if (!face_id.filename.isEmpty()) { + QString fileName = QFile::decodeName(face_id.filename); + if (face_id.filename.startsWith(":qmemoryfonts/")) { + // from qfontdatabase.cpp + QByteArray idx = face_id.filename; + idx.remove(0, 14); // remove ':qmemoryfonts/' + bool ok = false; + newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); + if (!ok) + newFreetype->fontData = QByteArray(); + } else if (!QFileInfo(fileName).isNativePath()) { + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + return 0; + } + newFreetype->fontData = file.readAll(); + } + } else { + newFreetype->fontData = fontData; + } + if (!newFreetype->fontData.isEmpty()) { + if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) { + return 0; + } + } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) { + return 0; + } + newFreetype->face = face; + + newFreetype->hbFace = 0; + newFreetype->hbFace_destroy_func = 0; + + newFreetype->ref.store(1); + newFreetype->xsize = 0; + newFreetype->ysize = 0; + newFreetype->matrix.xx = 0x10000; + newFreetype->matrix.yy = 0x10000; + newFreetype->matrix.xy = 0; + newFreetype->matrix.yx = 0; + newFreetype->unicode_map = 0; + newFreetype->symbol_map = 0; + + memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache)); + + for (int i = 0; i < newFreetype->face->num_charmaps; ++i) { + FT_CharMap cm = newFreetype->face->charmaps[i]; + switch(cm->encoding) { + case FT_ENCODING_UNICODE: + newFreetype->unicode_map = cm; + break; + case FT_ENCODING_APPLE_ROMAN: + case FT_ENCODING_ADOBE_LATIN_1: + if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE) + newFreetype->unicode_map = cm; + break; + case FT_ENCODING_ADOBE_CUSTOM: + case FT_ENCODING_MS_SYMBOL: + if (!newFreetype->symbol_map) + newFreetype->symbol_map = cm; + break; + default: + break; + } + } + + if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1) + FT_Set_Char_Size (face, X_SIZE(newFreetype->face, 0), Y_SIZE(newFreetype->face, 0), 0, 0); + + FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map); + QT_TRY { + freetypeData->faces.insert(face_id, newFreetype.data()); + } QT_CATCH(...) { + newFreetype.take()->release(face_id); + // we could return null in principle instead of throwing + QT_RETHROW; + } + freetype = newFreetype.take(); + } + return freetype; +} + +void QFreetypeFace::cleanup() +{ + if (hbFace && hbFace_destroy_func) { + hbFace_destroy_func(hbFace); + hbFace = 0; + } + FT_Done_Face(face); + face = 0; +} + +void QFreetypeFace::release(const QFontEngine::FaceId &face_id) +{ + if (!ref.deref()) { + if (face) { + QtFreetypeData *freetypeData = qt_getFreetypeData(); + + cleanup(); + + if (freetypeData->faces.contains(face_id)) + freetypeData->faces.take(face_id); + + if (freetypeData->faces.isEmpty()) { + FT_Done_FreeType(freetypeData->library); + freetypeData->library = 0; + } + } + + delete this; + } +} + + +void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing) +{ + *ysize = qRound(fontDef.pixelSize * 64); + *xsize = *ysize * fontDef.stretch / 100; + *outline_drawing = false; + + /* + * Bitmap only faces must match exactly, so find the closest + * one (height dominant search) + */ + if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) { + int best = 0; + for (int i = 1; i < face->num_fixed_sizes; i++) { + if (qAbs(*ysize - Y_SIZE(face,i)) < + qAbs (*ysize - Y_SIZE(face, best)) || + (qAbs (*ysize - Y_SIZE(face, i)) == + qAbs (*ysize - Y_SIZE(face, best)) && + qAbs (*xsize - X_SIZE(face, i)) < + qAbs (*xsize - X_SIZE(face, best)))) { + best = i; + } + } + if (FT_Set_Char_Size (face, X_SIZE(face, best), Y_SIZE(face, best), 0, 0) == 0) { + *xsize = X_SIZE(face, best); + *ysize = Y_SIZE(face, best); + } else { + int err = 1; + if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && ysize == 0 && face->num_fixed_sizes >= 1) { + // work around FT 2.1.10 problem with BDF without PIXEL_SIZE property + err = FT_Set_Pixel_Sizes(face, face->available_sizes[0].width, face->available_sizes[0].height); + if (err && face->num_fixed_sizes == 1) + err = 0; //even more of a workaround... + } + + if (err) + *xsize = *ysize = 0; + } + } else { + *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); + } +} + +QFontEngine::Properties QFreetypeFace::properties() const +{ + QFontEngine::Properties p; + p.postscriptName = FT_Get_Postscript_Name(face); + PS_FontInfoRec font_info; + if (FT_Get_PS_Font_Info(face, &font_info) == 0) + p.copyright = font_info.notice; + if (FT_IS_SCALABLE(face)) { + p.ascent = face->ascender; + p.descent = -face->descender; + p.leading = face->height - face->ascender + face->descender; + p.emSquare = face->units_per_EM; + p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax, + face->bbox.xMax - face->bbox.xMin, + face->bbox.yMax - face->bbox.yMin); + } else { + p.ascent = QFixed::fromFixed(face->size->metrics.ascender); + p.descent = QFixed::fromFixed(-face->size->metrics.descender); + p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender); + p.emSquare = face->size->metrics.y_ppem; +// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.); + p.boundingBox = QRectF(0, -p.ascent.toReal(), + face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() ); + } + p.italicAngle = 0; + p.capHeight = p.ascent; + p.lineWidth = face->underline_thickness; + return p; +} + +bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const +{ + return ft_getSfntTable(face, tag, buffer, length); +} + +/* Some fonts (such as MingLiu rely on hinting to scale different + components to their correct sizes. While this is really broken (it + should be done in the component glyph itself, not the hinter) we + will have to live with it. + + This means we can not use FT_LOAD_NO_HINTING to get the glyph + outline. All we can do is to load the unscaled glyph and scale it + down manually when required. +*/ +static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale) +{ + x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM); + y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM); + FT_Vector *p = g->outline.points; + const FT_Vector *e = p + g->outline.n_points; + while (p < e) { + p->x = FT_MulFix(p->x, x_scale); + p->y = FT_MulFix(p->y, y_scale); + ++p; + } +} + +#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug +void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale) +{ + const qreal factor = 1/64.; + scaleOutline(face, g, x_scale, y_scale); + + QPointF cp = point.toPointF(); + + // convert the outline to a painter path + int i = 0; + for (int j = 0; j < g->outline.n_contours; ++j) { + int last_point = g->outline.contours[j]; + GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point; + QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); + if (!(g->outline.tags[i] & 1)) { // start point is not on curve: + if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve: + GLYPH2PATH_DEBUG() << " start and end point are not on curve"; + start = (QPointF(g->outline.points[last_point].x*factor, + -g->outline.points[last_point].y*factor) + start) / 2.0; + } else { + GLYPH2PATH_DEBUG() << " end point is on curve, start is not"; + start = QPointF(g->outline.points[last_point].x*factor, + -g->outline.points[last_point].y*factor); + } + --i; // to use original start point as control point below + } + start += cp; + GLYPH2PATH_DEBUG() << " start at" << start; + + path->moveTo(start); + QPointF c[4]; + c[0] = start; + int n = 1; + while (i < last_point) { + ++i; + c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor); + GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i] + << ": on curve =" << (bool)(g->outline.tags[i] & 1); + ++n; + switch (g->outline.tags[i] & 3) { + case 2: + // cubic bezier element + if (n < 4) + continue; + c[3] = (c[3] + c[2])/2; + --i; + break; + case 0: + // quadratic bezier element + if (n < 3) + continue; + c[3] = (c[1] + c[2])/2; + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + --i; + break; + case 1: + case 3: + if (n == 2) { + GLYPH2PATH_DEBUG() << " lineTo" << c[1]; + path->lineTo(c[1]); + c[0] = c[1]; + n = 1; + continue; + } else if (n == 3) { + c[3] = c[2]; + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + } + break; + } + GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3]; + path->cubicTo(c[1], c[2], c[3]); + c[0] = c[3]; + n = 1; + } + + if (n == 1) { + GLYPH2PATH_DEBUG() << " closeSubpath"; + path->closeSubpath(); + } else { + c[3] = start; + if (n == 2) { + c[2] = (2*c[1] + c[3])/3; + c[1] = (2*c[1] + c[0])/3; + } + GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3]; + path->cubicTo(c[1], c[2], c[3]); + } + ++i; + } +} + +extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path); + +void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path, bool) +{ + if (slot->format != FT_GLYPH_FORMAT_BITMAP + || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO) + return; + + QPointF cp = point.toPointF(); + qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY), + slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path); +} + +QFontEngineFT::Glyph::~Glyph() +{ + delete [] data; +} + +static const uint subpixel_filter[3][3] = { + { 180, 60, 16 }, + { 38, 180, 38 }, + { 16, 60, 180 } +}; + +static inline uint filterPixel(uint red, uint green, uint blue, bool legacyFilter) +{ + uint res; + if (legacyFilter) { + uint high = (red*subpixel_filter[0][0] + green*subpixel_filter[0][1] + blue*subpixel_filter[0][2]) >> 8; + uint mid = (red*subpixel_filter[1][0] + green*subpixel_filter[1][1] + blue*subpixel_filter[1][2]) >> 8; + uint low = (red*subpixel_filter[2][0] + green*subpixel_filter[2][1] + blue*subpixel_filter[2][2]) >> 8; + res = (mid << 24) + (high << 16) + (mid << 8) + low; + } else { + uint alpha = green; + res = (alpha << 24) + (red << 16) + (green << 8) + blue; + } + return res; +} + +static void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) +{ + int h = height; + const int offs = bgr ? -1 : 1; + const int w = width * 3; + while (h--) { + uint *dd = dst; + for (int x = 0; x < w; x += 3) { + uint red = src[x+1-offs]; + uint green = src[x+1]; + uint blue = src[x+1+offs]; + *dd = filterPixel(red, green, blue, legacyFilter); + ++dd; + } + dst += width; + src += src_pitch; + } +} + +static void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr, bool legacyFilter) +{ + int h = height; + const int offs = bgr ? -src_pitch : src_pitch; + while (h--) { + for (int x = 0; x < width; x++) { + uint red = src[x+src_pitch-offs]; + uint green = src[x+src_pitch]; + uint blue = src[x+src_pitch+offs]; + dst[x] = filterPixel(red, green, blue, legacyFilter); + } + dst += width; + src += 3*src_pitch; + } +} + +static void convertGRAYToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch) { + for (int y = 0; y < height; ++y) { + int readpos = (y * src_pitch); + int writepos = (y * width); + for (int x = 0; x < width; ++x) { + dst[writepos + x] = (0xFF << 24) + (src[readpos + x] << 16) + (src[readpos + x] << 8) + src[readpos + x]; + } + } +} + +static void convoluteBitmap(const uchar *src, uchar *dst, int width, int height, int pitch) +{ + // convolute the bitmap with a triangle filter to get rid of color fringes + // If we take account for a gamma value of 2, we end up with + // weights of 1, 4, 9, 4, 1. We use an approximation of 1, 3, 8, 3, 1 here, + // as this nicely sums up to 16 :) + int h = height; + while (h--) { + dst[0] = dst[1] = 0; + // + for (int x = 2; x < width - 2; ++x) { + uint sum = src[x-2] + 3*src[x-1] + 8*src[x] + 3*src[x+1] + src[x+2]; + dst[x] = (uchar) (sum >> 4); + } + dst[width - 2] = dst[width - 1] = 0; + src += pitch; + dst += pitch; + } +} + +QFontEngineFT::QFontEngineFT(const QFontDef &fd) + : QFontEngine(Freetype) +{ + fontDef = fd; + matrix.xx = 0x10000; + matrix.yy = 0x10000; + matrix.xy = 0; + matrix.yx = 0; + cache_cost = 100; + kerning_pairs_loaded = false; + transform = false; + embolden = false; + obliquen = false; + antialias = true; + freetype = 0; + default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; +#ifndef Q_OS_WIN + default_hint_style = HintNone; +#else + default_hint_style = HintFull; +#endif + subpixelType = Subpixel_None; + lcdFilterType = 0; +#if defined(FT_LCD_FILTER_H) + lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT); +#endif + defaultFormat = Format_None; + embeddedbitmap = false; + const QByteArray env = qgetenv("QT_NO_FT_CACHE"); + cacheEnabled = env.isEmpty() || env.toInt() == 0; + m_subPixelPositionCount = 4; +} + +QFontEngineFT::~QFontEngineFT() +{ + if (freetype) + freetype->release(face_id); +} + +bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, + const QByteArray &fontData) +{ + return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData)); +} + +bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, + QFreetypeFace *freetypeFace) +{ + freetype = freetypeFace; + if (!freetype) { + xsize = 0; + ysize = 0; + return false; + } + defaultFormat = format; + this->antialias = antialias; + + if (!antialias) + glyphFormat = QFontEngine::Format_Mono; + else + glyphFormat = defaultFormat; + + face_id = faceId; + + symbol = freetype->symbol_map != 0; + PS_FontInfoRec psrec; + // don't assume that type1 fonts are symbol fonts by default + if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) { + symbol = bool(fontDef.family.contains(QLatin1String("symbol"), Qt::CaseInsensitive)); + } + + lbearing = rbearing = SHRT_MIN; + freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing); + + FT_Face face = lockFace(); + + if (FT_IS_SCALABLE(face)) { + bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC); + if (fake_oblique) { +#if !defined(Q_HAS_FT_GLYPHSLOT_OBLIQUE) + matrix.xy = 0x10000*3/10; + transform = true; +#else + obliquen = true; +#endif + } + FT_Set_Transform(face, &matrix, 0); + freetype->matrix = matrix; + // fake bold + if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face)) + embolden = true; + // underline metrics + line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)); + underline_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale)); + } else { + // ad hoc algorithm + int score = fontDef.weight * fontDef.pixelSize; + line_thickness = score / 700; + // looks better with thicker line for small pointsizes + if (line_thickness < 2 && score >= 1050) + line_thickness = 2; + underline_position = ((line_thickness * 2) + 3) / 6; + } + if (line_thickness < 1) + line_thickness = 1; + + metrics = face->size->metrics; + + /* + TrueType fonts with embedded bitmaps may have a bitmap font specific + ascent/descent in the EBLC table. There is no direct public API + to extract those values. The only way we've found is to trick freetype + into thinking that it's not a scalable font in FT_SelectSize so that + the metrics are retrieved from the bitmap strikes. + */ + if (FT_IS_SCALABLE(face)) { + for (int i = 0; i < face->num_fixed_sizes; ++i) { + if (xsize == X_SIZE(face, i) && ysize == Y_SIZE(face, i)) { + face->face_flags &= ~FT_FACE_FLAG_SCALABLE; + + FT_Select_Size(face, i); + metrics.ascender = face->size->metrics.ascender; + metrics.descender = face->size->metrics.descender; + FT_Set_Char_Size(face, xsize, ysize, 0, 0); + + face->face_flags |= FT_FACE_FLAG_SCALABLE; + break; + } + } + } + + fontDef.styleName = QString::fromUtf8(face->style_name); + + if (!freetype->hbFace) { + faceData.user_data = face; + faceData.get_font_table = ft_getSfntTable; + freetype->hbFace = harfbuzzFace(); + freetype->hbFace_destroy_func = face_destroy_func; + } else { + Q_ASSERT(!face_); + face_ = freetype->hbFace; + } + face_destroy_func = 0; // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it + + unlockFace(); + + fsType = freetype->fsType(); + return true; +} + +void QFontEngineFT::setDefaultHintStyle(HintStyle style) +{ + default_hint_style = style; +} + +int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags, + bool &hsubpixel, int &vfactor) const +{ + int load_flags = FT_LOAD_DEFAULT | default_load_flags; + int load_target = default_hint_style == HintLight + ? FT_LOAD_TARGET_LIGHT + : FT_LOAD_TARGET_NORMAL; + + if (format == Format_Mono) { + load_target = FT_LOAD_TARGET_MONO; + } else if (format == Format_A32) { + if (subpixelType == QFontEngineFT::Subpixel_RGB || subpixelType == QFontEngineFT::Subpixel_BGR) { + if (default_hint_style == HintFull) + load_target = FT_LOAD_TARGET_LCD; + hsubpixel = true; + } else if (subpixelType == QFontEngineFT::Subpixel_VRGB || subpixelType == QFontEngineFT::Subpixel_VBGR) { + if (default_hint_style == HintFull) + load_target = FT_LOAD_TARGET_LCD_V; + vfactor = 3; + } + } + + if (set && set->outline_drawing) + load_flags = FT_LOAD_NO_BITMAP; + + if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing)) + load_flags |= FT_LOAD_NO_HINTING; + else + load_flags |= load_target; + + return load_flags; +} + +QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, + QFixed subPixelPosition, + GlyphFormat format, + bool fetchMetricsOnly) const +{ +// Q_ASSERT(freetype->lock == 1); + + if (format == Format_None) { + if (defaultFormat != Format_None) { + format = defaultFormat; + } else { + format = Format_Mono; + } + } + + Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : 0; + if (g && g->format == format && (fetchMetricsOnly || g->data)) + return g; + + QFontEngineFT::GlyphInfo info; + + Q_ASSERT(format != Format_None); + bool hsubpixel = false; + int vfactor = 1; + int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor); + + if (format != Format_Mono && !embeddedbitmap) + load_flags |= FT_LOAD_NO_BITMAP; + + FT_Matrix matrix = freetype->matrix; + bool transform = matrix.xx != 0x10000 + || matrix.yy != 0x10000 + || matrix.xy != 0 + || matrix.yx != 0; + + if (transform) + load_flags |= FT_LOAD_NO_BITMAP; + + FT_Face face = freetype->face; + + FT_Vector v; + v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.toReal() * 64); + v.y = 0; + FT_Set_Transform(face, &freetype->matrix, &v); + + FT_Error err = FT_Load_Glyph(face, glyph, load_flags); + if (err && (load_flags & FT_LOAD_NO_BITMAP)) { + load_flags &= ~FT_LOAD_NO_BITMAP; + err = FT_Load_Glyph(face, glyph, load_flags); + } + if (err == FT_Err_Too_Few_Arguments) { + // this is an error in the bytecode interpreter, just try to run without it + load_flags |= FT_LOAD_FORCE_AUTOHINT; + err = FT_Load_Glyph(face, glyph, load_flags); + } + if (err != FT_Err_Ok) + qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph); + + FT_GlyphSlot slot = face->glyph; + + if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(slot); + if (obliquen) { + Q_FT_GLYPHSLOT_OBLIQUE(slot); + + // While Embolden alters the metrics of the slot, oblique does not, so we need + // to fix this ourselves. + transform = true; + FT_Matrix m; + m.xx = 0x10000; + m.yx = 0x0; + m.xy = 0x6000; + m.yy = 0x10000; + + FT_Matrix_Multiply(&m, &matrix); + } + + FT_Library library = qt_getFreetype(); + + info.xOff = TRUNC(ROUND(slot->advance.x)); + info.yOff = 0; + + if ((set && set->outline_drawing) || fetchMetricsOnly) { + int left = FLOOR(slot->metrics.horiBearingX); + int right = CEIL(slot->metrics.horiBearingX + slot->metrics.width); + int top = CEIL(slot->metrics.horiBearingY); + int bottom = FLOOR(slot->metrics.horiBearingY - slot->metrics.height); + int width = right-left; + int height = top-bottom; + + // If any of the metrics are too large to fit, don't cache them + if (qAbs(info.xOff) >= 128 + || qAbs(TRUNC(top)) >= 128 + || TRUNC(width) >= 256 + || TRUNC(height) >= 256 + || qAbs(TRUNC(left)) >= 128 + || qAbs(TRUNC(ROUND(slot->advance.x))) >= 128) { + return 0; + } + + g = new Glyph; + g->data = 0; + g->linearAdvance = slot->linearHoriAdvance >> 10; + g->width = TRUNC(width); + g->height = TRUNC(height); + g->x = TRUNC(left); + g->y = TRUNC(top); + g->advance = TRUNC(ROUND(slot->advance.x)); + g->format = format; + + if (set) + set->setGlyph(glyph, subPixelPosition, g); + + return g; + } + + uchar *glyph_buffer = 0; + int glyph_buffer_size = 0; +#if defined(QT_USE_FREETYPE_LCDFILTER) + bool useFreetypeRenderGlyph = false; + if (slot->format == FT_GLYPH_FORMAT_OUTLINE && (hsubpixel || vfactor != 1)) { + err = FT_Library_SetLcdFilter(library, (FT_LcdFilter)lcdFilterType); + if (err == FT_Err_Ok) + useFreetypeRenderGlyph = true; + } + + if (useFreetypeRenderGlyph) { + err = FT_Render_Glyph(slot, hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V); + + if (err != FT_Err_Ok) + qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph); + + FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE); + + info.height = slot->bitmap.rows / vfactor; + info.width = hsubpixel ? slot->bitmap.width / 3 : slot->bitmap.width; + info.x = -slot->bitmap_left; + info.y = slot->bitmap_top; + + glyph_buffer_size = info.width * info.height * 4; + glyph_buffer = new uchar[glyph_buffer_size]; + + if (hsubpixel) + convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, slot->bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_RGB, false); + else if (vfactor != 1) + convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, slot->bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_VRGB, false); + } else +#endif + { + int left = slot->metrics.horiBearingX; + int right = slot->metrics.horiBearingX + slot->metrics.width; + int top = slot->metrics.horiBearingY; + int bottom = slot->metrics.horiBearingY - slot->metrics.height; + if(transform && slot->format != FT_GLYPH_FORMAT_BITMAP) { + int l, r, t, b; + FT_Vector vector; + vector.x = left; + vector.y = top; + FT_Vector_Transform(&vector, &matrix); + l = r = vector.x; + t = b = vector.y; + vector.x = right; + vector.y = top; + FT_Vector_Transform(&vector, &matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + vector.x = right; + vector.y = bottom; + FT_Vector_Transform(&vector, &matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + vector.x = left; + vector.y = bottom; + FT_Vector_Transform(&vector, &matrix); + if (l > vector.x) l = vector.x; + if (r < vector.x) r = vector.x; + if (t < vector.y) t = vector.y; + if (b > vector.y) b = vector.y; + left = l; + right = r; + top = t; + bottom = b; + } + left = FLOOR(left); + right = CEIL(right); + bottom = FLOOR(bottom); + top = CEIL(top); + + int hpixels = TRUNC(right - left); + // subpixel position requires one more pixel + if (subPixelPosition > 0 && format != Format_Mono) + hpixels++; + + if (hsubpixel) + hpixels = hpixels*3 + 8; + info.width = hpixels; + info.height = TRUNC(top - bottom); + info.x = -TRUNC(left); + info.y = TRUNC(top); + if (hsubpixel) { + info.width /= 3; + info.x += 1; + } + + bool large_glyph = (((short)(slot->linearHoriAdvance>>10) != slot->linearHoriAdvance>>10) + || ((uchar)(info.width) != info.width) + || ((uchar)(info.height) != info.height) + || ((signed char)(info.x) != info.x) + || ((signed char)(info.y) != info.y) + || ((signed char)(info.xOff) != info.xOff)); + + if (large_glyph) { + delete [] glyph_buffer; + return 0; + } + + int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 : + (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4)); + glyph_buffer_size = pitch * info.height; + glyph_buffer = new uchar[glyph_buffer_size]; + + if (slot->format == FT_GLYPH_FORMAT_OUTLINE) { + FT_Bitmap bitmap; + bitmap.rows = info.height*vfactor; + bitmap.width = hpixels; + bitmap.pitch = format == Format_Mono ? (((info.width + 31) & ~31) >> 3) : ((bitmap.width + 3) & ~3); + if (!hsubpixel && vfactor == 1 && format != Format_A32) + bitmap.buffer = glyph_buffer; + else + bitmap.buffer = new uchar[bitmap.rows*bitmap.pitch]; + memset(bitmap.buffer, 0, bitmap.rows*bitmap.pitch); + bitmap.pixel_mode = format == Format_Mono ? FT_PIXEL_MODE_MONO : FT_PIXEL_MODE_GRAY; + FT_Matrix matrix; + matrix.xx = (hsubpixel ? 3 : 1) << 16; + matrix.yy = vfactor << 16; + matrix.yx = matrix.xy = 0; + + FT_Outline_Transform(&slot->outline, &matrix); + FT_Outline_Translate (&slot->outline, (hsubpixel ? -3*left +(4<<6) : -left), -bottom*vfactor); + FT_Outline_Get_Bitmap(library, &slot->outline, &bitmap); + if (hsubpixel) { + Q_ASSERT (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY); + Q_ASSERT(antialias); + uchar *convoluted = new uchar[bitmap.rows*bitmap.pitch]; + bool useLegacyLcdFilter = false; +#if defined(FC_LCD_FILTER) && defined(FT_LCD_FILTER_H) + useLegacyLcdFilter = (lcdFilterType == FT_LCD_FILTER_LEGACY); +#endif + uchar *buffer = bitmap.buffer; + if (!useLegacyLcdFilter) { + convoluteBitmap(bitmap.buffer, convoluted, bitmap.width, info.height, bitmap.pitch); + buffer = convoluted; + } + convertRGBToARGB(buffer + 1, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_RGB, useLegacyLcdFilter); + delete [] convoluted; + } else if (vfactor != 1) { + convertRGBToARGB_V(bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch, subpixelType != QFontEngineFT::Subpixel_VRGB, true); + } else if (format == Format_A32 && bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { + convertGRAYToARGB(bitmap.buffer, (uint *)glyph_buffer, info.width, info.height, bitmap.pitch); + } + + if (bitmap.buffer != glyph_buffer) + delete [] bitmap.buffer; + } else if (slot->format == FT_GLYPH_FORMAT_BITMAP) { + Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO); + uchar *src = slot->bitmap.buffer; + uchar *dst = glyph_buffer; + int h = slot->bitmap.rows; + if (format == Format_Mono) { + int bytes = ((info.width + 7) & ~7) >> 3; + while (h--) { + memcpy (dst, src, bytes); + dst += pitch; + src += slot->bitmap.pitch; + } + } else { + if (hsubpixel) { + while (h--) { + uint *dd = (uint *)dst; + *dd++ = 0; + for (int x = 0; x < slot->bitmap.width; x++) { + uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); + *dd++ = a; + } + *dd++ = 0; + dst += pitch; + src += slot->bitmap.pitch; + } + } else if (vfactor != 1) { + while (h--) { + uint *dd = (uint *)dst; + for (int x = 0; x < slot->bitmap.width; x++) { + uint a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffff : 0x000000); + *dd++ = a; + } + dst += pitch; + src += slot->bitmap.pitch; + } + } else { + while (h--) { + for (int x = 0; x < slot->bitmap.width; x++) { + unsigned char a = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00); + dst[x] = a; + } + dst += pitch; + src += slot->bitmap.pitch; + } + } + } + } else { + qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format); + delete [] glyph_buffer; + return 0; + } + } + + + if (!g) { + g = new Glyph; + g->data = 0; + } + + g->linearAdvance = slot->linearHoriAdvance >> 10; + g->width = info.width; + g->height = info.height; + g->x = -info.x; + g->y = info.y; + g->advance = info.xOff; + g->format = format; + delete [] g->data; + g->data = glyph_buffer; + + if (set) + set->setGlyph(glyph, subPixelPosition, g); + + return g; +} + +QFontEngine::FaceId QFontEngineFT::faceId() const +{ + return face_id; +} + +QFontEngine::Properties QFontEngineFT::properties() const +{ + Properties p = freetype->properties(); + if (p.postscriptName.isEmpty()) { + p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family.toUtf8()); + } + + return freetype->properties(); +} + +QFixed QFontEngineFT::emSquareSize() const +{ + if (FT_IS_SCALABLE(freetype->face)) + return freetype->face->units_per_EM; + else + return freetype->face->size->metrics.y_ppem; +} + +bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + return ft_getSfntTable(freetype->face, tag, buffer, length); +} + +int QFontEngineFT::synthesized() const +{ + int s = 0; + if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)) + s = SynthesizedItalic; + if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD)) + s |= SynthesizedBold; + if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face)) + s |= SynthesizedStretch; + return s; +} + +QFixed QFontEngineFT::ascent() const +{ + return QFixed::fromFixed(metrics.ascender); +} + +QFixed QFontEngineFT::descent() const +{ + return QFixed::fromFixed(-metrics.descender); +} + +QFixed QFontEngineFT::leading() const +{ + return QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender); +} + +QFixed QFontEngineFT::xHeight() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->sxHeight) { + lockFace(); + QFixed answer = QFixed(os2->sxHeight*freetype->face->size->metrics.y_ppem)/freetype->face->units_per_EM; + unlockFace(); + return answer; + } + return QFontEngine::xHeight(); +} + +QFixed QFontEngineFT::averageCharWidth() const +{ + TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2); + if (os2 && os2->xAvgCharWidth) { + lockFace(); + QFixed answer = QFixed(os2->xAvgCharWidth*freetype->face->size->metrics.x_ppem)/freetype->face->units_per_EM; + unlockFace(); + return answer; + } + return QFontEngine::averageCharWidth(); +} + +qreal QFontEngineFT::maxCharWidth() const +{ + return metrics.max_advance >> 6; +} + +static const ushort char_table[] = { + 40, + 67, + 70, + 75, + 86, + 88, + 89, + 91, + 95, + 102, + 114, + 124, + 127, + 205, + 645, + 884, + 922, + 1070, + 12386 +}; + +static const int char_table_entries = sizeof(char_table)/sizeof(ushort); + + +qreal QFontEngineFT::minLeftBearing() const +{ + if (lbearing == SHRT_MIN) + (void) minRightBearing(); // calculates both + return lbearing.toReal(); +} + +qreal QFontEngineFT::minRightBearing() const +{ + if (rbearing == SHRT_MIN) { + lbearing = rbearing = 0; + for (int i = 0; i < char_table_entries; ++i) { + const glyph_t glyph = glyphIndex(char_table[i]); + if (glyph != 0) { + glyph_metrics_t gi = const_cast(this)->boundingBox(glyph); + lbearing = qMin(lbearing, gi.x); + rbearing = qMin(rbearing, (gi.xoff - gi.x - gi.width)); + } + } + } + return rbearing.toReal(); +} + +QFixed QFontEngineFT::lineThickness() const +{ + return line_thickness; +} + +QFixed QFontEngineFT::underlinePosition() const +{ + return underline_position; +} + +void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const +{ + if (!kerning_pairs_loaded) { + kerning_pairs_loaded = true; + lockFace(); + if (freetype->face->size->metrics.x_ppem != 0) { + QFixed scalingFactor(freetype->face->units_per_EM/freetype->face->size->metrics.x_ppem); + unlockFace(); + const_cast(this)->loadKerningPairs(scalingFactor); + } else { + unlockFace(); + } + } + + if (shouldUseDesignMetrics(flags) && !(fontDef.styleStrategy & QFont::ForceIntegerMetrics)) + flags |= DesignMetrics; + else + flags &= ~DesignMetrics; + + QFontEngine::doKerning(g, flags); +} + +QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransform &matrix) +{ + if (matrix.type() > QTransform::TxShear) + return 0; + + // FT_Set_Transform only supports scalable fonts + if (!FT_IS_SCALABLE(freetype->face)) + return 0; + + FT_Matrix m; + m.xx = FT_Fixed(matrix.m11() * 65536); + m.xy = FT_Fixed(-matrix.m21() * 65536); + m.yx = FT_Fixed(-matrix.m12() * 65536); + m.yy = FT_Fixed(matrix.m22() * 65536); + + QGlyphSet *gs = 0; + + for (int i = 0; i < transformedGlyphSets.count(); ++i) { + const QGlyphSet &g = transformedGlyphSets.at(i); + if (g.transformationMatrix.xx == m.xx + && g.transformationMatrix.xy == m.xy + && g.transformationMatrix.yx == m.yx + && g.transformationMatrix.yy == m.yy) { + + // found a match, move it to the front + transformedGlyphSets.move(i, 0); + gs = &transformedGlyphSets[0]; + break; + } + } + + if (!gs) { + // don't try to load huge fonts + bool draw_as_outline = fontDef.pixelSize * qSqrt(qAbs(matrix.det())) >= QT_MAX_CACHED_GLYPH_SIZE; + if (draw_as_outline) + return 0; + + // don't cache more than 10 transformations + if (transformedGlyphSets.count() >= 10) { + transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0); + } else { + transformedGlyphSets.prepend(QGlyphSet()); + } + gs = &transformedGlyphSets[0]; + gs->clear(); + gs->transformationMatrix = m; + gs->outline_drawing = draw_as_outline; + } + + return gs; +} + +bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs, + const QFixedPoint *positions, + GlyphFormat format) +{ + FT_Face face = 0; + + for (int i = 0; i < num_glyphs; ++i) { + QFixed spp = subPixelPositionForX(positions[i].x); + Glyph *glyph = gs ? gs->getGlyph(glyphs[i], spp) : 0; + if (glyph == 0 || glyph->format != format) { + if (!face) { + face = lockFace(); + FT_Matrix m = matrix; + FT_Matrix_Multiply(&gs->transformationMatrix, &m); + FT_Set_Transform(face, &m, 0); + freetype->matrix = m; + } + if (!loadGlyph(gs, glyphs[i], spp, format)) { + unlockFace(); + return false; + } + } + } + + if (face) + unlockFace(); + + return true; +} + +void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) +{ + FT_Face face = lockFace(Unscaled); + FT_Set_Transform(face, 0, 0); + FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); + + int left = face->glyph->metrics.horiBearingX; + int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width; + int top = face->glyph->metrics.horiBearingY; + int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height; + + QFixedPoint p; + p.x = 0; + p.y = 0; + + metrics->width = QFixed::fromFixed(right-left); + metrics->height = QFixed::fromFixed(top-bottom); + metrics->x = QFixed::fromFixed(left); + metrics->y = QFixed::fromFixed(-top); + metrics->xoff = QFixed::fromFixed(face->glyph->advance.x); + + if (!FT_IS_SCALABLE(freetype->face)) + QFreetypeFace::addBitmapToPath(face->glyph, p, path); + else + QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6); + + FT_Set_Transform(face, &freetype->matrix, 0); + unlockFace(); +} + +bool QFontEngineFT::supportsTransformation(const QTransform &transform) const +{ + // The freetype engine falls back to QFontEngine for tranformed glyphs, + // which uses fast-tranform and produces very ugly results, so we claim + // to support just translations. + return transform.type() <= QTransform::TxTranslate; +} + +void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) +{ + if (!glyphs.numGlyphs) + return; + + if (FT_IS_SCALABLE(freetype->face)) { + QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); + } else { + QVarLengthArray positions; + QVarLengthArray positioned_glyphs; + QTransform matrix; + matrix.translate(x, y); + getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions); + + FT_Face face = lockFace(Unscaled); + for (int gl = 0; gl < glyphs.numGlyphs; gl++) { + FT_UInt glyph = positioned_glyphs[gl]; + FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO); + freetype->addBitmapToPath(face->glyph, positions[gl], path); + } + unlockFace(); + } +} + +void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, + QPainterPath *path, QTextItem::RenderFlags) +{ + FT_Face face = lockFace(Unscaled); + + for (int gl = 0; gl < numGlyphs; gl++) { + FT_UInt glyph = glyphs[gl]; + + FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP); + + FT_GlyphSlot g = face->glyph; + if (g->format != FT_GLYPH_FORMAT_OUTLINE) + continue; + if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(g); + if (obliquen) Q_FT_GLYPHSLOT_OBLIQUE(g); + QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize); + } + unlockFace(); +} + +glyph_t QFontEngineFT::glyphIndex(uint ucs4) const +{ + glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0; + if (glyph == 0) { + FT_Face face = freetype->face; + glyph = FT_Get_Char_Index(face, ucs4); + if (glyph == 0) { + // Certain fonts don't have no-break space and tab, + // while we usually want to render them as space + if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) { + glyph = FT_Get_Char_Index(face, QChar::Space); + } else if (freetype->symbol_map) { + // Symbol fonts can have more than one CMAPs, FreeType should take the + // correct one for us by default, so we always try FT_Get_Char_Index + // first. If it didn't work (returns 0), we will explicitly set the + // CMAP to symbol font one and try again. symbol_map is not always the + // correct one because in certain fonts like Wingdings symbol_map only + // contains PUA codepoints instead of the common ones. + FT_Set_Charmap(face, freetype->symbol_map); + glyph = FT_Get_Char_Index(face, ucs4); + FT_Set_Charmap(face, freetype->unicode_map); + } + } + if (ucs4 < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[ucs4] = glyph; + } + + return glyph; +} + +bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, + QFontEngine::ShaperFlags flags) const +{ + Q_ASSERT(glyphs->numGlyphs >= *nglyphs); + if (*nglyphs < len) { + *nglyphs = len; + return false; + } + + int glyph_pos = 0; + if (freetype->symbol_map) { + FT_Face face = freetype->face; + QStringIterator it(str, str + len); + while (it.hasNext()) { + uint uc = it.next(); + glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; + if ( !glyphs->glyphs[glyph_pos] ) { + // Symbol fonts can have more than one CMAPs, FreeType should take the + // correct one for us by default, so we always try FT_Get_Char_Index + // first. If it didn't work (returns 0), we will explicitly set the + // CMAP to symbol font one and try again. symbol_map is not always the + // correct one because in certain fonts like Wingdings symbol_map only + // contains PUA codepoints instead of the common ones. + glyph_t glyph = FT_Get_Char_Index(face, uc); + // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9), + // while we usually want to render them as space + if (!glyph && (uc == 0xa0 || uc == 0x9)) { + uc = 0x20; + glyph = FT_Get_Char_Index(face, uc); + } + if (!glyph) { + FT_Set_Charmap(face, freetype->symbol_map); + glyph = FT_Get_Char_Index(face, uc); + FT_Set_Charmap(face, freetype->unicode_map); + } + glyphs->glyphs[glyph_pos] = glyph; + if (uc < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[uc] = glyph; + } + ++glyph_pos; + } + } else { + FT_Face face = freetype->face; + QStringIterator it(str, str + len); + while (it.hasNext()) { + uint uc = it.next(); + glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0; + if (!glyphs->glyphs[glyph_pos]) { + { + redo: + glyph_t glyph = FT_Get_Char_Index(face, uc); + if (!glyph && (uc == 0xa0 || uc == 0x9)) { + uc = 0x20; + goto redo; + } + glyphs->glyphs[glyph_pos] = glyph; + if (uc < QFreetypeFace::cmapCacheSize) + freetype->cmapCache[uc] = glyph; + } + } + ++glyph_pos; + } + } + + *nglyphs = glyph_pos; + glyphs->numGlyphs = glyph_pos; + + if (!(flags & GlyphIndicesOnly)) + recalcAdvances(glyphs, flags); + + return true; +} + +bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const +{ + if (!FT_IS_SCALABLE(freetype->face)) + return false; + + return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics); +} + +void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const +{ + FT_Face face = 0; + bool design = shouldUseDesignMetrics(flags); + for (int i = 0; i < glyphs->numGlyphs; i++) { + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : 0; + // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph + GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono; + if (g && g->format == acceptableFormat) { + glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); + } else { + if (!face) + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs->glyphs[i], 0, Format_None, true); + glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10) + : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round(); + if (!cacheEnabled) + delete g; + } + } + if (face) + unlockFace(); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < glyphs->numGlyphs; ++i) + glyphs->advances[i] = glyphs->advances[i].round(); + } +} + +glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) +{ + FT_Face face = 0; + + glyph_metrics_t overall; + // initialize with line height, we get the same behaviour on all platforms + overall.y = -ascent(); + overall.height = ascent() + descent(); + + QFixed ymax = 0; + QFixed xmax = 0; + for (int i = 0; i < glyphs.numGlyphs; i++) { + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : 0; + if (!g) { + if (!face) + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyphs.glyphs[i], 0, Format_None, true); + } + if (g) { + QFixed x = overall.xoff + glyphs.offsets[i].x + g->x; + QFixed y = overall.yoff + glyphs.offsets[i].y - g->y; + overall.x = qMin(overall.x, x); + overall.y = qMin(overall.y, y); + xmax = qMax(xmax, x + g->width); + ymax = qMax(ymax, y + g->height); + overall.xoff += g->advance; + if (!cacheEnabled) + delete g; + } else { + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left)); + QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top); + overall.x = qMin(overall.x, x); + overall.y = qMin(overall.y, y); + xmax = qMax(xmax, x + TRUNC(right - left)); + ymax = qMax(ymax, y + TRUNC(top - bottom)); + overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x))); + } + } + overall.height = qMax(overall.height, ymax - overall.y); + overall.width = xmax - overall.x; + + if (face) + unlockFace(); + + return overall; +} + +glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) +{ + FT_Face face = 0; + glyph_metrics_t overall; + Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : 0; + if (!g) { + face = lockFace(); + g = loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, glyph, 0, Format_None, true); + } + if (g) { + overall.x = g->x; + overall.y = -g->y; + overall.width = g->width; + overall.height = g->height; + overall.xoff = g->advance; + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + overall.xoff = overall.xoff.round(); + if (!cacheEnabled) + delete g; + } else { + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + overall.width = TRUNC(right-left); + overall.height = TRUNC(top-bottom); + overall.x = TRUNC(left); + overall.y = -TRUNC(top); + overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); + } + if (face) + unlockFace(); + return overall; +} + +glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix) +{ + return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None); +} + +static FT_Matrix QTransformToFTMatrix(const QTransform &matrix) +{ + FT_Matrix m; + + m.xx = FT_Fixed(matrix.m11() * 65536); + m.xy = FT_Fixed(-matrix.m21() * 65536); + m.yx = FT_Fixed(-matrix.m12() * 65536); + m.yy = FT_Fixed(matrix.m22() * 65536); + + return m; +} + +glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format) +{ + FT_Face face = 0; + glyph_metrics_t overall; + QGlyphSet *glyphSet = 0; + FT_Matrix ftMatrix = QTransformToFTMatrix(matrix); + if (cacheEnabled) { + if (matrix.type() > QTransform::TxTranslate && FT_IS_SCALABLE(freetype->face)) { + // TODO move everything here to a method of its own to access glyphSets + // to be shared with a new method that will replace loadTransformedGlyphSet() + for (int i = 0; i < transformedGlyphSets.count(); ++i) { + const QGlyphSet &g = transformedGlyphSets.at(i); + if (g.transformationMatrix.xx == ftMatrix.xx + && g.transformationMatrix.xy == ftMatrix.xy + && g.transformationMatrix.yx == ftMatrix.yx + && g.transformationMatrix.yy == ftMatrix.yy) { + + // found a match, move it to the front + transformedGlyphSets.move(i, 0); + glyphSet = &transformedGlyphSets[0]; + break; + } + } + + if (!glyphSet) { + // don't cache more than 10 transformations + if (transformedGlyphSets.count() >= 10) { + transformedGlyphSets.move(transformedGlyphSets.size() - 1, 0); + } else { + transformedGlyphSets.prepend(QGlyphSet()); + } + glyphSet = &transformedGlyphSets[0]; + glyphSet->clear(); + glyphSet->transformationMatrix = ftMatrix; + } + Q_ASSERT(glyphSet); + } else { + glyphSet = &defaultGlyphSet; + } + } + Glyph * g = glyphSet ? glyphSet->getGlyph(glyph, subPixelPosition) : 0; + if (!g || g->format != format) { + face = lockFace(); + FT_Matrix m = this->matrix; + FT_Matrix_Multiply(&ftMatrix, &m); + freetype->matrix = m; + g = loadGlyph(glyphSet, glyph, subPixelPosition, format, false); + } + + if (g) { + overall.x = g->x; + overall.y = -g->y; + overall.width = g->width; + overall.height = g->height; + overall.xoff = g->advance; + if (!glyphSet) + delete g; + } else { + int left = FLOOR(face->glyph->metrics.horiBearingX); + int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); + int top = CEIL(face->glyph->metrics.horiBearingY); + int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height); + + overall.width = TRUNC(right-left); + overall.height = TRUNC(top-bottom); + overall.x = TRUNC(left); + overall.y = -TRUNC(top); + overall.xoff = TRUNC(ROUND(face->glyph->advance.x)); + } + if (face) + unlockFace(); + return overall; +} + +QImage *QFontEngineFT::lockedAlphaMapForGlyph(glyph_t glyphIndex, QFixed subPixelPosition, + QFontEngine::GlyphFormat neededFormat, + const QTransform &t, QPoint *offset) +{ + Q_ASSERT(currentlyLockedAlphaMap.isNull()); + lockFace(); + + if (isBitmapFont()) + neededFormat = Format_Mono; + else if (neededFormat == Format_None && defaultFormat != Format_None) + neededFormat = defaultFormat; + else if (neededFormat == Format_None) + neededFormat = Format_A8; + + QImage::Format format; + switch (neededFormat) { + case Format_Mono: + format = QImage::Format_Mono; + break; + case Format_A8: + format = QImage::Format_Indexed8; + break; + case Format_A32: + format = QImage::Format_ARGB32; + break; + default: + Q_ASSERT(false); + format = QImage::Format_Invalid; + }; + + QFontEngineFT::Glyph *glyph; + QScopedPointer glyphGuard; + if (cacheEnabled) { + QFontEngineFT::QGlyphSet *gset = &defaultGlyphSet; + QFontEngine::HintStyle hintStyle = default_hint_style; + if (t.type() >= QTransform::TxScale) { + // disable hinting if the glyphs are transformed + default_hint_style = HintNone; + if (t.isAffine()) + gset = loadTransformedGlyphSet(t); + else + gset = 0; + } + + if (gset) { + FT_Matrix m = matrix; + FT_Matrix_Multiply(&gset->transformationMatrix, &m); + FT_Set_Transform(freetype->face, &m, 0); + freetype->matrix = m; + } + + if (!gset || gset->outline_drawing || !loadGlyph(gset, glyphIndex, subPixelPosition, + neededFormat)) { + default_hint_style = hintStyle; + return QFontEngine::lockedAlphaMapForGlyph(glyphIndex, subPixelPosition, neededFormat, t, + offset); + } + default_hint_style = hintStyle; + + glyph = gset->getGlyph(glyphIndex, subPixelPosition); + } else { + FT_Matrix m = matrix; + FT_Matrix extra = QTransformToFTMatrix(t); + FT_Matrix_Multiply(&extra, &m); + FT_Set_Transform(freetype->face, &m, 0); + freetype->matrix = m; + glyph = loadGlyph(0, glyphIndex, subPixelPosition, neededFormat); + glyphGuard.reset(glyph); + } + + if (glyph == 0 || glyph->data == 0 || glyph->width == 0 || glyph->height == 0) { + unlockFace(); + return 0; + } + + int pitch; + switch (neededFormat) { + case Format_Mono: + pitch = ((glyph->width + 31) & ~31) >> 3; + break; + case Format_A8: + pitch = (glyph->width + 3) & ~3; + break; + case Format_A32: + pitch = glyph->width * 4; + break; + default: + Q_ASSERT(false); + pitch = 0; + }; + + if (offset != 0) + *offset = QPoint(glyph->x, -glyph->y); + + currentlyLockedAlphaMap = QImage(glyph->data, glyph->width, glyph->height, pitch, format); + if (!glyphGuard.isNull()) + currentlyLockedAlphaMap = currentlyLockedAlphaMap.copy(); + Q_ASSERT(!currentlyLockedAlphaMap.isNull()); + + QImageData *data = currentlyLockedAlphaMap.data_ptr(); + data->is_locked = true; + + return ¤tlyLockedAlphaMap; +} + +void QFontEngineFT::unlockAlphaMapForGlyph() +{ + Q_ASSERT(!currentlyLockedAlphaMap.isNull()); + unlockFace(); + currentlyLockedAlphaMap = QImage(); +} + +QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, QFixed subPixelPosition, GlyphFormat format) +{ + return defaultGlyphSet.outline_drawing ? 0 : + loadGlyph(cacheEnabled ? &defaultGlyphSet : 0, g, subPixelPosition, format); +} + +QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition) +{ + lockFace(); + + QScopedPointer glyph(loadGlyphFor(g, subPixelPosition, antialias ? Format_A8 : Format_Mono)); + if (!glyph || !glyph->data) { + unlockFace(); + return QFontEngine::alphaMapForGlyph(g); + } + + const int pitch = antialias ? (glyph->width + 3) & ~3 : ((glyph->width + 31)/32) * 4; + + QImage img(glyph->width, glyph->height, antialias ? QImage::Format_Indexed8 : QImage::Format_Mono); + if (antialias) { + QVector colors(256); + for (int i=0; i<256; ++i) + colors[i] = qRgba(0, 0, 0, i); + img.setColorTable(colors); + } else { + QVector colors(2); + colors[0] = qRgba(0, 0, 0, 0); + colors[1] = qRgba(0, 0, 0, 255); + img.setColorTable(colors); + } + Q_ASSERT(img.bytesPerLine() == pitch); + if (glyph->width) { + for (int y = 0; y < glyph->height; ++y) + memcpy(img.scanLine(y), &glyph->data[y * pitch], pitch); + } + if (cacheEnabled) + glyph.take(); + unlockFace(); + + return img; +} + +QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t) +{ + if (t.type() > QTransform::TxTranslate) + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); + + lockFace(); + + QScopedPointer glyph(loadGlyphFor(g, subPixelPosition, Format_A32)); + if (!glyph || !glyph->data) { + unlockFace(); + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t); + } + + QImage img(glyph->width, glyph->height, QImage::Format_RGB32); + memcpy(img.bits(), glyph->data, 4 * glyph->width * glyph->height); + + if (cacheEnabled) + glyph.take(); + unlockFace(); + + return img; +} + +void QFontEngineFT::removeGlyphFromCache(glyph_t glyph) +{ + defaultGlyphSet.removeGlyphFromCache(glyph, 0); +} + +int QFontEngineFT::glyphCount() const +{ + int count = 0; + FT_Face face = lockFace(); + if (face) { + count = face->num_glyphs; + unlockFace(); + } + return count; +} + +FT_Face QFontEngineFT::lockFace(Scaling scale) const +{ + freetype->lock(); + FT_Face face = freetype->face; + if (scale == Unscaled) { + FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0); + freetype->xsize = face->units_per_EM << 6; + freetype->ysize = face->units_per_EM << 6; + } else if (freetype->xsize != xsize || freetype->ysize != ysize) { + FT_Set_Char_Size(face, xsize, ysize, 0, 0); + freetype->xsize = xsize; + freetype->ysize = ysize; + } + if (freetype->matrix.xx != matrix.xx || + freetype->matrix.yy != matrix.yy || + freetype->matrix.xy != matrix.xy || + freetype->matrix.yx != matrix.yx) { + freetype->matrix = matrix; + FT_Set_Transform(face, &freetype->matrix, 0); + } + + return face; +} + +void QFontEngineFT::unlockFace() const +{ + freetype->unlock(); +} + +FT_Face QFontEngineFT::non_locked_face() const +{ + return freetype->face; +} + + +QFontEngineFT::QGlyphSet::QGlyphSet() + : outline_drawing(false) +{ + transformationMatrix.xx = 0x10000; + transformationMatrix.yy = 0x10000; + transformationMatrix.xy = 0; + transformationMatrix.yx = 0; + memset(fast_glyph_data, 0, sizeof(fast_glyph_data)); + fast_glyph_count = 0; +} + +QFontEngineFT::QGlyphSet::~QGlyphSet() +{ + clear(); +} + +void QFontEngineFT::QGlyphSet::clear() +{ + if (fast_glyph_count > 0) { + for (int i = 0; i < 256; ++i) { + if (fast_glyph_data[i]) { + delete fast_glyph_data[i]; + fast_glyph_data[i] = 0; + } + } + fast_glyph_count = 0; + } + qDeleteAll(glyph_data); + glyph_data.clear(); +} + +void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition) +{ + if (useFastGlyphData(index, subPixelPosition)) { + if (fast_glyph_data[index]) { + delete fast_glyph_data[index]; + fast_glyph_data[index] = 0; + if (fast_glyph_count > 0) + --fast_glyph_count; + } + } else { + delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition)); + } +} + +void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph) +{ + if (useFastGlyphData(index, subPixelPosition)) { + if (!fast_glyph_data[index]) + ++fast_glyph_count; + fast_glyph_data[index] = glyph; + } else { + glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph); + } +} + +int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) +{ + lockFace(); + bool hsubpixel = true; + int vfactor = 1; + int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor); + int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints); + unlockFace(); + return result; +} + +bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe) +{ + if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype)) + return false; + + // Increase the reference of this QFreetypeFace since one more QFontEngineFT + // will be using it + freetype->ref.ref(); + + default_load_flags = fe->default_load_flags; + default_hint_style = fe->default_hint_style; + antialias = fe->antialias; + transform = fe->transform; + embolden = fe->embolden; + obliquen = fe->obliquen; + subpixelType = fe->subpixelType; + lcdFilterType = fe->lcdFilterType; + embeddedbitmap = fe->embeddedbitmap; + + return true; +} + +QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const +{ + QFontDef fontDef(this->fontDef); + fontDef.pixelSize = pixelSize; + QFontEngineFT *fe = new QFontEngineFT(fontDef); + if (!fe->initFromFontEngine(this)) { + delete fe; + return 0; + } else { + return fe; + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_FREETYPE diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/gui/text/qtextengine.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/gui/text/qtextengine.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/gui/text/qtextengine.cpp 2014-06-19 12:08:06.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/gui/text/qtextengine.cpp 2014-08-13 04:35:08.420435869 +0200 @@ -1549,6 +1549,7 @@ case QChar::Script_Hiragana: case QChar::Script_Katakana: case QChar::Script_Bopomofo: + case QChar::Script_Gujarati: analysis[i].script = QChar::Script_Common; break; default: diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp 2014-06-19 12:08:08.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp 2014-08-13 04:35:08.411435885 +0200 @@ -929,6 +929,15 @@ i += 2; } + if (rmod_masks.meta) { + // translate Super/Hyper keys to Meta if we're using them as the MetaModifier + if (rmod_masks.meta == rmod_masks.super && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) { + code = Qt::Key_Meta; + } else if (rmod_masks.meta == rmod_masks.hyper && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) { + code = Qt::Key_Meta; + } + } + return code; } diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/plugins/platforms/xcb/qxcbscreen.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/plugins/platforms/xcb/qxcbscreen.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/plugins/platforms/xcb/qxcbscreen.cpp 2014-06-19 12:08:08.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/plugins/platforms/xcb/qxcbscreen.cpp 2014-08-13 04:35:08.400435918 +0200 @@ -106,6 +106,11 @@ qDebug(" root ID........: %x", screen()->root); #endif + QScopedPointer rootAttribs( + xcb_get_window_attributes_reply(xcb_connection(), + xcb_get_window_attributes_unchecked(xcb_connection(), screen()->root), NULL)); + const quint32 existingEventMask = rootAttribs.isNull() ? 0 : rootAttribs->your_event_mask; + const quint32 mask = XCB_CW_EVENT_MASK; const quint32 values[] = { // XCB_CW_EVENT_MASK @@ -113,6 +118,7 @@ | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY // for the "MANAGER" atom (system tray notification). + | existingEventMask // don't overwrite the event mask on the root window }; xcb_change_window_attributes(xcb_connection(), screen()->root, mask, values); diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp 2014-06-19 12:08:08.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/plugins/platforms/xcb/qxcbxsettings.cpp 2014-08-13 04:35:08.418435872 +0200 @@ -42,6 +42,7 @@ #include "qxcbxsettings.h" #include +#include #include @@ -149,47 +150,67 @@ { if (xSettings.length() < 12) return; - // we ignore byteorder for now - char byteOrder = xSettings.at(1); - Q_UNUSED(byteOrder); - uint number_of_settings = *reinterpret_cast(xSettings.mid(8,4).constData()); + char byteOrder = xSettings.at(0); + if (byteOrder != LSBFirst && byteOrder != MSBFirst) { + qWarning("%s ByteOrder byte %d not 0 or 1", Q_FUNC_INFO , byteOrder); + return; + } + +#define ADJUST_BO(b, t, x) \ + ((b == LSBFirst) ? \ + qFromLittleEndian((const uchar *)(x)) : \ + qFromBigEndian((const uchar *)(x))) +#define VALIDATE_LENGTH(x) \ + if ((size_t)xSettings.length() < (offset + local_offset + 12 + x)) { \ + qWarning("%s Length %d runs past end of data", Q_FUNC_INFO , x); \ + return; \ + } + uint number_of_settings = ADJUST_BO(byteOrder, quint32, xSettings.mid(8,4).constData()); const char *data = xSettings.constData() + 12; size_t offset = 0; for (uint i = 0; i < number_of_settings; i++) { int local_offset = 0; + VALIDATE_LENGTH(2); XSettingsType type = static_cast(*reinterpret_cast(data + offset)); local_offset += 2; - quint16 name_len = *reinterpret_cast(data + offset + local_offset); + VALIDATE_LENGTH(2); + quint16 name_len = ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset += 2; + VALIDATE_LENGTH(name_len); QByteArray name(data + offset + local_offset, name_len); local_offset += round_to_nearest_multiple_of_4(name_len); - int last_change_serial = *reinterpret_cast(data + offset + local_offset); + VALIDATE_LENGTH(4); + int last_change_serial = ADJUST_BO(byteOrder, qint32, data + offset + local_offset); Q_UNUSED(last_change_serial); local_offset += 4; QVariant value; if (type == XSettingsTypeString) { - int value_length = *reinterpret_cast(data + offset + local_offset); + VALIDATE_LENGTH(4); + int value_length = ADJUST_BO(byteOrder, qint32, data + offset + local_offset); local_offset+=4; + VALIDATE_LENGTH(value_length); QByteArray value_string(data + offset + local_offset, value_length); value.setValue(value_string); local_offset += round_to_nearest_multiple_of_4(value_length); } else if (type == XSettingsTypeInteger) { - int value_length = *reinterpret_cast(data + offset + local_offset); + VALIDATE_LENGTH(4); + int value_length = ADJUST_BO(byteOrder, qint32, data + offset + local_offset); local_offset += 4; value.setValue(value_length); } else if (type == XSettingsTypeColor) { - quint16 red = *reinterpret_cast(data + offset + local_offset); + VALIDATE_LENGTH(2*4); + quint16 red = ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset += 2; - quint16 green = *reinterpret_cast(data + offset + local_offset); + quint16 green = ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset += 2; - quint16 blue = *reinterpret_cast(data + offset + local_offset); + quint16 blue = ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset += 2; - quint16 alpha= *reinterpret_cast(data + offset + local_offset); + quint16 alpha= ADJUST_BO(byteOrder, quint16, data + offset + local_offset); local_offset += 2; QColor color_value(red,green,blue,alpha); value.setValue(color_value); diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/widgets/util/qsystemtrayicon.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/widgets/util/qsystemtrayicon.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/widgets/util/qsystemtrayicon.cpp 2014-06-19 12:08:03.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/widgets/util/qsystemtrayicon.cpp 2014-08-13 04:35:08.413435882 +0200 @@ -672,6 +672,74 @@ QWidget::timerEvent(e); } +////////////////////////////////////////////////////////////////////// +void QSystemTrayIconPrivate::install_sys_qpa() +{ + qpa_sys->init(); + QObject::connect(qpa_sys, SIGNAL(activated(QPlatformSystemTrayIcon::ActivationReason)), + q_func(), SLOT(_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason))); + QObject::connect(qpa_sys, &QPlatformSystemTrayIcon::messageClicked, + q_func(), &QSystemTrayIcon::messageClicked); + updateMenu_sys(); + updateIcon_sys(); + updateToolTip_sys(); +} + +void QSystemTrayIconPrivate::remove_sys_qpa() +{ + qpa_sys->cleanup(); +} + +QRect QSystemTrayIconPrivate::geometry_sys_qpa() const +{ + return qpa_sys->geometry(); +} + +void QSystemTrayIconPrivate::updateIcon_sys_qpa() +{ + qpa_sys->updateIcon(icon); +} + +void QSystemTrayIconPrivate::updateMenu_sys_qpa() +{ + if (menu) { + if (!menu->platformMenu()) { + QPlatformMenu *platformMenu = qpa_sys->createMenu(); + if (platformMenu) + menu->setPlatformMenu(platformMenu); + } + qpa_sys->updateMenu(menu->platformMenu()); + } +} + +void QSystemTrayIconPrivate::updateToolTip_sys_qpa() +{ + qpa_sys->updateToolTip(toolTip); +} + +void QSystemTrayIconPrivate::showMessage_sys_qpa(const QString &message, + const QString &title, + QSystemTrayIcon::MessageIcon icon, + int msecs) +{ + QIcon notificationIcon; + switch (icon) { + case QSystemTrayIcon::Information: + notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); + break; + case QSystemTrayIcon::Warning: + notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); + break; + case QSystemTrayIcon::Critical: + notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); + break; + default: + break; + } + qpa_sys->showMessage(message, title, notificationIcon, + static_cast(icon), msecs); +} + QT_END_NAMESPACE #endif // QT_NO_SYSTEMTRAYICON diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/widgets/util/qsystemtrayicon_p.h qt-everywhere-opensource-src-5.3.1.new/qtbase/src/widgets/util/qsystemtrayicon_p.h --- qt-everywhere-opensource-src-5.3.1/qtbase/src/widgets/util/qsystemtrayicon_p.h 2014-06-19 12:08:03.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/widgets/util/qsystemtrayicon_p.h 2014-08-13 04:35:08.414435881 +0200 @@ -98,6 +98,15 @@ QSystemTrayIconSys *sys; QPlatformSystemTrayIcon *qpa_sys; bool visible; + +private: + void install_sys_qpa(); + void remove_sys_qpa(); + void updateIcon_sys_qpa(); + void updateToolTip_sys_qpa(); + void updateMenu_sys_qpa(); + QRect geometry_sys_qpa() const; + void showMessage_sys_qpa(const QString &msg, const QString &title, QSystemTrayIcon::MessageIcon icon, int secs); }; class QBalloonTip : public QWidget diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/widgets/util/qsystemtrayicon_qpa.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/widgets/util/qsystemtrayicon_qpa.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/widgets/util/qsystemtrayicon_qpa.cpp 2014-06-19 12:08:03.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/widgets/util/qsystemtrayicon_qpa.cpp 2014-08-13 04:35:08.414435881 +0200 @@ -65,28 +65,20 @@ void QSystemTrayIconPrivate::install_sys() { - if (qpa_sys) { - qpa_sys->init(); - QObject::connect(qpa_sys, SIGNAL(activated(QPlatformSystemTrayIcon::ActivationReason)), - q_func(), SLOT(_q_emitActivated(QPlatformSystemTrayIcon::ActivationReason))); - QObject::connect(qpa_sys, SIGNAL(messageClicked()), - q_func(), SIGNAL(messageClicked())); - updateMenu_sys(); - updateIcon_sys(); - updateToolTip_sys(); - } + if (qpa_sys) + install_sys_qpa(); } void QSystemTrayIconPrivate::remove_sys() { if (qpa_sys) - qpa_sys->cleanup(); + remove_sys_qpa(); } QRect QSystemTrayIconPrivate::geometry_sys() const { if (qpa_sys) - return qpa_sys->geometry(); + return geometry_sys_qpa(); else return QRect(); } @@ -94,25 +86,19 @@ void QSystemTrayIconPrivate::updateIcon_sys() { if (qpa_sys) - qpa_sys->updateIcon(icon); + updateIcon_sys_qpa(); } void QSystemTrayIconPrivate::updateMenu_sys() { - if (qpa_sys && menu) { - if (!menu->platformMenu()) { - QPlatformMenu *platformMenu = qpa_sys->createMenu(); - if (platformMenu) - menu->setPlatformMenu(platformMenu); - } - qpa_sys->updateMenu(menu->platformMenu()); - } + if (qpa_sys) + updateMenu_sys_qpa(); } void QSystemTrayIconPrivate::updateToolTip_sys() { if (qpa_sys) - qpa_sys->updateToolTip(toolTip); + updateToolTip_sys_qpa(); } bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys() @@ -138,25 +124,8 @@ QSystemTrayIcon::MessageIcon icon, int msecs) { - if (!qpa_sys) - return; - - QIcon notificationIcon; - switch (icon) { - case QSystemTrayIcon::Information: - notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); - break; - case QSystemTrayIcon::Warning: - notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning); - break; - case QSystemTrayIcon::Critical: - notificationIcon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); - break; - default: - break; - } - qpa_sys->showMessage(message, title, notificationIcon, - static_cast(icon), msecs); + if (qpa_sys) + showMessage_sys_qpa(message, title, icon, msecs); } QT_END_NAMESPACE diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/src/widgets/util/qsystemtrayicon_x11.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/src/widgets/util/qsystemtrayicon_x11.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/src/widgets/util/qsystemtrayicon_x11.cpp 2014-06-19 12:08:03.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/src/widgets/util/qsystemtrayicon_x11.cpp 2014-08-13 04:35:08.414435881 +0200 @@ -55,6 +55,9 @@ #include #include #include +#include +#include +#include #include #ifndef QT_NO_SYSTEMTRAYICON @@ -209,16 +212,22 @@ QSystemTrayIconPrivate::QSystemTrayIconPrivate() : sys(0), + qpa_sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()), visible(false) { } QSystemTrayIconPrivate::~QSystemTrayIconPrivate() { + delete qpa_sys; } void QSystemTrayIconPrivate::install_sys() { + if (qpa_sys) { + install_sys_qpa(); + return; + } Q_Q(QSystemTrayIcon); if (!sys && locateSystemTray()) { sys = new QSystemTrayIconSys(q); @@ -229,6 +238,8 @@ QRect QSystemTrayIconPrivate::geometry_sys() const { + if (qpa_sys) + return geometry_sys_qpa(); if (!sys) return QRect(); return sys->globalGeometry(); @@ -236,6 +247,10 @@ void QSystemTrayIconPrivate::remove_sys() { + if (qpa_sys) { + remove_sys_qpa(); + return; + } if (!sys) return; QBalloonTip::hideBalloon(); @@ -246,17 +261,26 @@ void QSystemTrayIconPrivate::updateIcon_sys() { + if (qpa_sys) { + updateIcon_sys_qpa(); + return; + } if (sys) sys->updateIcon(); } void QSystemTrayIconPrivate::updateMenu_sys() { - + if (qpa_sys) + updateMenu_sys_qpa(); } void QSystemTrayIconPrivate::updateToolTip_sys() { + if (qpa_sys) { + updateToolTip_sys_qpa(); + return; + } if (!sys) return; #ifndef QT_NO_TOOLTIP @@ -266,6 +290,11 @@ bool QSystemTrayIconPrivate::isSystemTrayAvailable_sys() { + QScopedPointer sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()); + if (sys) + return sys->isSystemTrayAvailable(); + + // no QPlatformSystemTrayIcon so fall back to default xcb platform behavior const QString platform = QGuiApplication::platformName(); if (platform.compare(QStringLiteral("xcb"), Qt::CaseInsensitive) == 0) return locateSystemTray(); @@ -274,12 +303,21 @@ bool QSystemTrayIconPrivate::supportsMessages_sys() { + QScopedPointer sys(QGuiApplicationPrivate::platformTheme()->createPlatformSystemTrayIcon()); + if (sys) + return sys->supportsMessages(); + + // no QPlatformSystemTrayIcon so fall back to default xcb platform behavior return true; } void QSystemTrayIconPrivate::showMessage_sys(const QString &message, const QString &title, QSystemTrayIcon::MessageIcon icon, int msecs) { + if (qpa_sys) { + showMessage_sys_qpa(message, title, icon, msecs); + return; + } if (!sys) return; const QPoint g = sys->globalGeometry().topLeft(); diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/tests/auto/corelib/io/qdebug/qdebug.pro qt-everywhere-opensource-src-5.3.1.new/qtbase/tests/auto/corelib/io/qdebug/qdebug.pro --- qt-everywhere-opensource-src-5.3.1/qtbase/tests/auto/corelib/io/qdebug/qdebug.pro 2014-06-19 12:08:01.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/tests/auto/corelib/io/qdebug/qdebug.pro 2014-08-13 04:35:08.405435896 +0200 @@ -1,5 +1,5 @@ CONFIG += testcase parallel_test TARGET = tst_qdebug -QT = core testlib +QT = core testlib concurrent SOURCES = tst_qdebug.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff -Naur qt-everywhere-opensource-src-5.3.1/qtbase/tests/auto/corelib/io/qdebug/tst_qdebug.cpp qt-everywhere-opensource-src-5.3.1.new/qtbase/tests/auto/corelib/io/qdebug/tst_qdebug.cpp --- qt-everywhere-opensource-src-5.3.1/qtbase/tests/auto/corelib/io/qdebug/tst_qdebug.cpp 2014-06-19 12:08:01.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtbase/tests/auto/corelib/io/qdebug/tst_qdebug.cpp 2014-08-13 04:35:08.405435896 +0200 @@ -44,6 +44,9 @@ #include #include +#include +#include + class tst_QDebug: public QObject { Q_OBJECT @@ -59,6 +62,7 @@ void qDebugQLatin1String() const; void textStreamModifiers() const; void defaultMessagehandler() const; + void threadSafety() const; }; void tst_QDebug::assignment() const @@ -305,5 +309,41 @@ QVERIFY(same); } +QMutex s_mutex; +QStringList s_messages; +QSemaphore s_sema; + +static void threadSafeMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + QMutexLocker lock(&s_mutex); + s_messages.append(msg); + Q_UNUSED(type); + Q_UNUSED(context); +} + +static void doDebug() // called in each thread +{ + s_sema.acquire(); + qDebug() << "doDebug"; +} + +void tst_QDebug::threadSafety() const +{ + MessageHandlerSetter mhs(threadSafeMessageHandler); + const int numThreads = 10; + QThreadPool::globalInstance()->setMaxThreadCount(numThreads); + QFutureSynchronizer sync; + for (int i = 0; i < numThreads; ++i) { + sync.addFuture(QtConcurrent::run(&doDebug)); + } + s_sema.release(numThreads); + sync.waitForFinished(); + QMutexLocker lock(&s_mutex); + QCOMPARE(s_messages.count(), numThreads); + for (int i = 0; i < numThreads; ++i) { + QCOMPARE(s_messages.at(i), QStringLiteral("doDebug")); + } +} + QTEST_MAIN(tst_QDebug); #include "tst_qdebug.moc" diff -Naur qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/JavaScriptCore/JavaScriptCore.pri qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/JavaScriptCore/JavaScriptCore.pri --- qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/JavaScriptCore/JavaScriptCore.pri 2014-06-19 12:08:23.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/JavaScriptCore/JavaScriptCore.pri 2014-08-13 04:14:29.357784981 +0200 @@ -7,6 +7,12 @@ SOURCE_DIR = $${ROOT_WEBKIT_DIR}/Source/JavaScriptCore +equals(QT_ARCH, arm)|equals(QT_ARCH, aarch64)|equals(QT_ARCH, i386)|equals(QT_ARCH, i686)|equals(QT_ARCH, x86_64)| equals(QT_ARCH, powerpc64)|equals(QT_ARCH, powerpc) { + message("JavaScriptCore workaround for QtWebkit: do not build with -g, but with -g1") + QMAKE_CXXFLAGS_RELEASE -= -g + QMAKE_CXXFLAGS_RELEASE += -g1 +} + JAVASCRIPTCORE_GENERATED_SOURCES_DIR = $${ROOT_BUILD_DIR}/Source/JavaScriptCore/$${GENERATED_SOURCES_DESTDIR} INCLUDEPATH += \ diff -Naur qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/QtWebKit.pro qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/QtWebKit.pro --- qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/QtWebKit.pro 2014-06-19 12:08:26.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/QtWebKit.pro 2014-08-13 04:13:16.564919490 +0200 @@ -7,6 +7,11 @@ TEMPLATE = subdirs CONFIG += ordered +CONFIG(release):!CONFIG(standalone_package) { + contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols + unix:contains(QT_CONFIG, reduce_relocations):CONFIG += bsymbolic_functions +} + api.file = api.pri SUBDIRS += api diff -Naur qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/WebCore/WebCore.pri qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/WebCore/WebCore.pri --- qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/WebCore/WebCore.pri 2014-06-19 12:08:15.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/WebCore/WebCore.pri 2014-08-13 04:18:36.403328716 +0200 @@ -7,6 +7,12 @@ SOURCE_DIR = $${ROOT_WEBKIT_DIR}/Source/WebCore +equals(QT_ARCH, arm)|equals(QT_ARCH, aarch64)|equals(QT_ARCH, i386)|equals(QT_ARCH, i686)|equals(QT_ARCH, x86_64)| equals(QT_ARCH, powerpc64)|equals(QT_ARCH, powerpc) { + message("WebCore workaround for QtWebkit: do not build with -g, but with -g1") + QMAKE_CXXFLAGS_RELEASE -= -g + QMAKE_CXXFLAGS_RELEASE += -g1 +} + QT *= network sql core-private gui-private WEBCORE_GENERATED_SOURCES_DIR = $${ROOT_BUILD_DIR}/Source/WebCore/$${GENERATED_SOURCES_DESTDIR} @@ -301,6 +307,8 @@ unix:!mac:*-g++*:QMAKE_CXXFLAGS += -ffunction-sections } unix:!mac:*-g++*:QMAKE_CXXFLAGS += -fdata-sections +unix:!mac:*-g++*:!equals(QT_ARCH, powerpc):!equals(QT_ARCH, s390):!equals(QT_ARCH, mips):!equals(QT_ARCH, arm):QMAKE_LFLAGS += -Wl,--no-keep-memory +unix:!mac:*-g++*:!equals(QT_ARCH, powerpc):!equals(QT_ARCH, s390):!equals(QT_ARCH, mips):!equals(QT_ARCH, arm):QMAKE_LFLAGS += -Wl,--reduce-memory-overheads unix:!mac:*-g++*:QMAKE_LFLAGS += -Wl,--gc-sections linux*-g++*:QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF diff -Naur qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/WebCore/WebCore.pri.orig qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/WebCore/WebCore.pri.orig --- qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/WebCore/WebCore.pri.orig 1970-01-01 01:00:00.000000000 +0100 +++ qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/WebCore/WebCore.pri.orig 2014-08-13 04:15:02.022724637 +0200 @@ -0,0 +1,316 @@ +# ------------------------------------------------------------------- +# This file contains shared rules used both when building WebCore +# itself, and by targets that use WebCore. +# +# See 'Tools/qmake/README' for an overview of the build system +# ------------------------------------------------------------------- + +SOURCE_DIR = $${ROOT_WEBKIT_DIR}/Source/WebCore + +equals(QT_ARCH, arm)|equals(QT_ARCH, aarch64)|equals(QT_ARCH, i386)|equals(QT_ARCH, i686)|equals(QT_ARCH, x86_64)| equals(QT_ARCH, powerpc64)|equals(QT_ARCH, powerpc) { + message("WebCore workaround for QtWebkit: do not build with -g, but with -g1") + QMAKE_CXXFLAGS_RELEASE -= -g + QMAKE_CXXFLAGS_RELEASE += -g1 +} + +QT *= network sql core-private gui-private + +WEBCORE_GENERATED_SOURCES_DIR = $${ROOT_BUILD_DIR}/Source/WebCore/$${GENERATED_SOURCES_DESTDIR} + +INCLUDEPATH += \ + $$SOURCE_DIR \ + $$SOURCE_DIR/Modules/filesystem \ + $$SOURCE_DIR/Modules/geolocation \ + $$SOURCE_DIR/Modules/indexeddb \ + $$SOURCE_DIR/Modules/navigatorcontentutils \ + $$SOURCE_DIR/Modules/notifications \ + $$SOURCE_DIR/Modules/proximity \ + $$SOURCE_DIR/Modules/quota \ + $$SOURCE_DIR/Modules/webaudio \ + $$SOURCE_DIR/Modules/webdatabase \ + $$SOURCE_DIR/Modules/websockets \ + $$SOURCE_DIR/accessibility \ + $$SOURCE_DIR/bindings \ + $$SOURCE_DIR/bindings/generic \ + $$SOURCE_DIR/bridge \ + $$SOURCE_DIR/bridge/qt \ + $$SOURCE_DIR/css \ + $$SOURCE_DIR/dom \ + $$SOURCE_DIR/dom/default \ + $$SOURCE_DIR/editing \ + $$SOURCE_DIR/fileapi \ + $$SOURCE_DIR/history \ + $$SOURCE_DIR/html \ + $$SOURCE_DIR/html/canvas \ + $$SOURCE_DIR/html/forms \ + $$SOURCE_DIR/html/parser \ + $$SOURCE_DIR/html/shadow \ + $$SOURCE_DIR/html/track \ + $$SOURCE_DIR/inspector \ + $$SOURCE_DIR/loader \ + $$SOURCE_DIR/loader/appcache \ + $$SOURCE_DIR/loader/archive \ + $$SOURCE_DIR/loader/cache \ + $$SOURCE_DIR/loader/icon \ + $$SOURCE_DIR/mathml \ + $$SOURCE_DIR/page \ + $$SOURCE_DIR/page/animation \ + $$SOURCE_DIR/page/qt \ + $$SOURCE_DIR/page/scrolling \ + $$SOURCE_DIR/page/scrolling/coordinatedgraphics \ + $$SOURCE_DIR/platform \ + $$SOURCE_DIR/platform/animation \ + $$SOURCE_DIR/platform/audio \ + $$SOURCE_DIR/platform/graphics \ + $$SOURCE_DIR/platform/graphics/cpu/arm \ + $$SOURCE_DIR/platform/graphics/cpu/arm/filters \ + $$SOURCE_DIR/platform/graphics/filters \ + $$SOURCE_DIR/platform/graphics/filters/texmap \ + $$SOURCE_DIR/platform/graphics/opengl \ + $$SOURCE_DIR/platform/graphics/opentype \ + $$SOURCE_DIR/platform/graphics/qt \ + $$SOURCE_DIR/platform/graphics/surfaces \ + $$SOURCE_DIR/platform/graphics/texmap \ + $$SOURCE_DIR/platform/graphics/texmap/coordinated \ + $$SOURCE_DIR/platform/graphics/transforms \ + $$SOURCE_DIR/platform/image-decoders \ + $$SOURCE_DIR/platform/image-decoders/bmp \ + $$SOURCE_DIR/platform/image-decoders/ico \ + $$SOURCE_DIR/platform/image-decoders/gif \ + $$SOURCE_DIR/platform/image-decoders/jpeg \ + $$SOURCE_DIR/platform/image-decoders/png \ + $$SOURCE_DIR/platform/image-decoders/webp \ + $$SOURCE_DIR/platform/leveldb \ + $$SOURCE_DIR/platform/mock \ + $$SOURCE_DIR/platform/network \ + $$SOURCE_DIR/platform/network/qt \ + $$SOURCE_DIR/platform/qt \ + $$SOURCE_DIR/platform/sql \ + $$SOURCE_DIR/platform/text \ + $$SOURCE_DIR/platform/text/transcoder \ + $$SOURCE_DIR/plugins \ + $$SOURCE_DIR/rendering \ + $$SOURCE_DIR/rendering/mathml \ + $$SOURCE_DIR/rendering/shapes \ + $$SOURCE_DIR/rendering/style \ + $$SOURCE_DIR/rendering/svg \ + $$SOURCE_DIR/storage \ + $$SOURCE_DIR/svg \ + $$SOURCE_DIR/svg/animation \ + $$SOURCE_DIR/svg/graphics \ + $$SOURCE_DIR/svg/graphics/filters \ + $$SOURCE_DIR/svg/properties \ + $$SOURCE_DIR/testing \ + $$SOURCE_DIR/websockets \ + $$SOURCE_DIR/workers \ + $$SOURCE_DIR/xml \ + $$SOURCE_DIR/xml/parser \ + $$SOURCE_DIR/../ThirdParty + +INCLUDEPATH += \ + $$SOURCE_DIR/bridge/jsc \ + $$SOURCE_DIR/bindings/js \ + $$SOURCE_DIR/bridge/c \ + $$SOURCE_DIR/testing/js + +INCLUDEPATH += $$WEBCORE_GENERATED_SOURCES_DIR + +enable?(XSLT) { + use?(LIBXML2) { + mac { + QMAKE_CXXFLAGS += -iwithsysroot /usr/include/libxslt -iwithsysroot /usr/include/libxml2 + LIBS += -lxml2 -lxslt + } else { + PKGCONFIG += libxslt libxml-2.0 + } + } else { + QT *= xmlpatterns + } +} else:!mac:use?(LIBXML2) { + PKGCONFIG += libxml-2.0 +} + +use?(ZLIB) { + LIBS += -lz +} + +enable?(NETSCAPE_PLUGIN_API) { + unix { + mac { + INCLUDEPATH += platform/mac + # Note: XP_MACOSX is defined in npapi.h + } else { + xlibAvailable() { + CONFIG *= x11 + LIBS += -lXrender + DEFINES += MOZ_X11 + } + DEFINES += XP_UNIX + DEFINES += ENABLE_NETSCAPE_PLUGIN_METADATA_CACHE=1 + } + } + win32-* { + LIBS += \ + -ladvapi32 \ + -lgdi32 \ + -lshell32 \ + -lshlwapi \ + -luser32 \ + -lversion + } +} + +have?(qtsensors):if(enable?(ORIENTATION_EVENTS)|enable?(DEVICE_ORIENTATION)): QT += sensors + +use?(QT_MOBILITY_SYSTEMINFO) { + CONFIG *= mobility + MOBILITY *= systeminfo +} + +enable?(GAMEPAD) { + INCLUDEPATH += \ + $$SOURCE_DIR/platform/linux \ + $$SOURCE_DIR/Modules/gamepad +} + +use?(GLIB) { + PKGCONFIG *= glib-2.0 gio-2.0 +} + +use?(GSTREAMER) { + use?(GSTREAMER010) { + PKGCONFIG += gstreamer-0.10 gstreamer-app-0.10 gstreamer-base-0.10 gstreamer-interfaces-0.10 gstreamer-pbutils-0.10 gstreamer-plugins-base-0.10 gstreamer-video-0.10 + } else { + DEFINES += GST_API_VERSION=1.0 + DEFINES += GST_API_VERSION_1 + PKGCONFIG += gstreamer-1.0 gstreamer-app-1.0 gstreamer-base-1.0 gstreamer-pbutils-1.0 gstreamer-plugins-base-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 + } +} + +enable?(VIDEO) { + use?(GSTREAMER) { + INCLUDEPATH += $$SOURCE_DIR/platform/graphics/gstreamer + } else:use?(QT_MULTIMEDIA) { + QT *= multimedia + } +} + +enable?(WEB_AUDIO) { + use?(GSTREAMER) { + DEFINES += WTF_USE_WEBAUDIO_GSTREAMER=1 + INCLUDEPATH += $$SOURCE_DIR/platform/audio/gstreamer + use?(GSTREAMER010) { + PKGCONFIG += gstreamer-audio-0.10 gstreamer-fft-0.10 + } else { + PKGCONFIG += gstreamer-audio-1.0 gstreamer-fft-1.0 + } + } +} + +use?(3D_GRAPHICS) { + win32: { + mingw: { + # Make sure OpenGL libs are after the webcore lib so MinGW can resolve symbols + contains(QT_CONFIG, opengles2) { + CONFIG(debug, debug|release):contains(QT_CONFIG, angle) { + LIBS += $$QMAKE_LIBS_OPENGL_ES2_DEBUG + } else { + LIBS += $$QMAKE_LIBS_OPENGL_ES2 + } + } else { + LIBS += $$QMAKE_LIBS_OPENGL + } + } + } else { + contains(QT_CONFIG, opengles2): CONFIG += egl + } +} + +use?(GRAPHICS_SURFACE) { + mac: LIBS += -framework IOSurface -framework CoreFoundation + linux-*: { + LIBS += -lXcomposite -lXrender + CONFIG *= x11 + } +} + +have?(sqlite3) { + mac { + LIBS += -lsqlite3 + } else { + PKGCONFIG += sqlite3 + } +} else { + SQLITE3SRCDIR = $$(SQLITE3SRCDIR) + isEmpty(SQLITE3SRCDIR): SQLITE3SRCDIR = ../../../qtbase/src/3rdparty/sqlite/ + exists($${SQLITE3SRCDIR}/sqlite3.c) { + INCLUDEPATH += $${SQLITE3SRCDIR} + DEFINES += SQLITE_CORE SQLITE_OMIT_LOAD_EXTENSION SQLITE_OMIT_COMPLETE + } else { + INCLUDEPATH += $${SQLITE3SRCDIR} + LIBS += -lsqlite3 + } +} + +use?(system_leveldb): LIBS += -lleveldb -lmemenv + +use?(libjpeg): LIBS += -ljpeg +use?(libpng): LIBS += -lpng +use?(webp): LIBS += -lwebp + +enable?(opencl) { + LIBS += -lOpenCL + INCLUDEPATH += $$SOURCE_DIR/platform/graphics/gpu/opencl +} + +mac { + LIBS += -framework Carbon -framework AppKit -framework IOKit +} + +win32 { + INCLUDEPATH += $$SOURCE_DIR/platform/win + + wince* { + # see https://bugs.webkit.org/show_bug.cgi?id=43442 + DEFINES += HAVE_LOCALTIME_S=0 + + LIBS += -lmmtimer + LIBS += -lole32 + } + else { + LIBS += -lgdi32 + LIBS += -lole32 + LIBS += -luser32 + } +} + +# Remove whole program optimizations due to miscompilations +win32-msvc2005|win32-msvc2008|win32-msvc2010|win32-msvc2012|win32-msvc2013|wince*:{ + QMAKE_CFLAGS_LTCG -= -GL + QMAKE_CXXFLAGS_LTCG -= -GL + + # Disable incremental linking for windows 32bit OS debug build as WebKit is so big + # that linker failes to link incrementally in debug mode. + ARCH = $$(PROCESSOR_ARCHITECTURE) + WOW64ARCH = $$(PROCESSOR_ARCHITEW6432) + equals(ARCH, x86):{ + isEmpty(WOW64ARCH): QMAKE_LFLAGS_DEBUG += /INCREMENTAL:NO + } +} + +mac { + LIBS_PRIVATE += -framework Carbon -framework AppKit +} + +# -ffunction-section conflicts with -pg option +!contains(CONFIG, gprof) { + unix:!mac:*-g++*:QMAKE_CXXFLAGS += -ffunction-sections +} +unix:!mac:*-g++*:QMAKE_CXXFLAGS += -fdata-sections +unix:!mac:*-g++*:QMAKE_LFLAGS += -Wl,--gc-sections +linux*-g++*:QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF + +enable_fast_mobile_scrolling: DEFINES += ENABLE_FAST_MOBILE_SCROLLING=1 + +build?(qttestsupport):have?(FONTCONFIG): PKGCONFIG += fontconfig + diff -Naur qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/WebKit2/WebKit2.pri qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/WebKit2/WebKit2.pri --- qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/WebKit2/WebKit2.pri 2014-06-19 12:08:26.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/WebKit2/WebKit2.pri 2014-08-13 04:15:55.276626229 +0200 @@ -9,6 +9,12 @@ WEBKIT2_GENERATED_SOURCES_DIR = $${ROOT_BUILD_DIR}/Source/WebKit2/$${GENERATED_SOURCES_DESTDIR} +equals(QT_ARCH, arm)|equals(QT_ARCH, aarch64)|equals(QT_ARCH, i386)|equals(QT_ARCH, i686)|equals(QT_ARCH, x86_64)| equals(QT_ARCH, powerpc64)|equals(QT_ARCH, powerpc) { + message("WebKit2 workaround for QtWebkit: do not build with -g, but with -g1") + QMAKE_CXXFLAGS_RELEASE -= -g + QMAKE_CXXFLAGS_RELEASE += -g1 +} + INCLUDEPATH += \ $$SOURCE_DIR \ $$SOURCE_DIR/Platform \ diff -Naur qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/WTF/WTF.pri qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/WTF/WTF.pri --- qt-everywhere-opensource-src-5.3.1/qtwebkit/Source/WTF/WTF.pri 2014-06-19 12:08:13.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Source/WTF/WTF.pri 2014-08-13 04:17:09.728488903 +0200 @@ -8,6 +8,12 @@ # All external modules should include WTF headers by prefixing with "wtf" (#include ). INCLUDEPATH += $$PWD +equals(QT_ARCH, arm)|equals(QT_ARCH, aarch64)|equals(QT_ARCH, i386)|equals(QT_ARCH, i686)|equals(QT_ARCH, x86_64)| equals(QT_ARCH, powerpc64)|equals(QT_ARCH, powerpc) { + message("WTF workaround for QtWebkit: do not build with -g, but with -g1") + QMAKE_CXXFLAGS_RELEASE -= -g + QMAKE_CXXFLAGS_RELEASE += -g1 +} + mac { # Mac OS does ship libicu but not the associated header files. # Therefore WebKit provides adequate header files. diff -Naur qt-everywhere-opensource-src-5.3.1/qtwebkit/Tools/qmake/mkspecs/features/unix/default_post.prf qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Tools/qmake/mkspecs/features/unix/default_post.prf --- qt-everywhere-opensource-src-5.3.1/qtwebkit/Tools/qmake/mkspecs/features/unix/default_post.prf 2014-06-19 12:08:13.000000000 +0200 +++ qt-everywhere-opensource-src-5.3.1.new/qtwebkit/Tools/qmake/mkspecs/features/unix/default_post.prf 2014-08-13 04:18:05.571385661 +0200 @@ -38,7 +38,7 @@ linux-*g++* { !production_build { # Treat warnings as errors on x86/Linux/GCC - isEqual(QT_ARCH,x86_64)|isEqual(QT_ARCH,i386): QMAKE_CXXFLAGS += -Werror + #isEqual(QT_ARCH,x86_64)|isEqual(QT_ARCH,i386): QMAKE_CXXFLAGS += -Werror } } @@ -56,7 +56,7 @@ } } -contains(TEMPLATE, app): CONFIG += rpath +#contains(TEMPLATE, app): CONFIG += rpath CONFIG(debug, debug|release)|force_debug_info|separate_debug_info { # Make ld not cache the symbol tables of input files in memory to avoid memory exhaustion during the linking phase.