Inside Qt for Symbian QApplication/zh

From Qt Wiki
Jump to navigation Jump to search

作者: "habert":http://developer.qt.nokia.com/member/3776

Qt for Symbian之程序启动

我们都知道Qt中只要构造出QApplication就启动了,可启动细节就很少有人知道了,当然我们编写Qt的程序无需关心这么多,这也是为什么要用Qt的原因,但如果能够了解则可以加深对Qt的认识。今天看了一下Qt for Symbian的源码,整理如下,希望对有兴趣的朋友有帮助:

QApplication::QApplication(int &amp;argc, char **argv)<br /> : QCoreApplication('''new QApplicationPrivate(argc, argv, GuiClient) //第一步) //第二步<br />{<br /> Q_D(QApplication); //第三步<br /> d->construct(); //第四步<br />}


第一步构造了QApplicationPrivate的实例,其中GuiClient是一个枚举常量,表示应用程序的类型,这里我们不用关心。说道 QApplicationPrivate,如果你看过Qt的源码,你会发现很多类都配对有相应的Private类,这是Qt在封装底层实现时经常用的一种模式,Handle-Body Design Pattern,也就是说你在xx类中看不到具体的实现,具体的实现都封装在xxPrivate类中,而在xx类的方法只是调用xxPrivate中相应的方法。
第二步用QApplicationPrivate的实例构造了QCoreApplication,这里调用的是QCoreApplication的QCoreApplication::QCoreApplication(QCoreApplicationPrivate &p)构造函数,代码如下:

QCoreApplication::QCoreApplication(QCoreApplicationPrivate &amp;p)<br /> : QObject(p, 0)<br />{<br /> init();<br /> // note: it is the subclasses' job to call<br /> // QCoreApplicationPrivate::eventDispatcher->startingUp();<br />}


上面代码中的init初始化了一些资源,其中包括事件分发器。
第三步定义了d,这里是通过宏Q_D来实现的,其中宏Q_D的定义如下:

#define Q_D(Class) Class##Private''' const d = d_func()<code><br />所以这里的d即是调用父类构造函数QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)时创建的QApplicationPrivate实例。

第四步调用d的construct函数也就是QApplicationPrivate的实例的construct函数代码如下所示<br />

void QApplicationPrivate::construct(
#ifdef Q_WS_X11
Display *dpy, Qt::HANDLE visual, Qt::HANDLE cmap
#endif
)
{
initResources();

qt_is_gui_used = (qt_appType != QApplication::Tty);
process_cmdline();
// the environment variable has the lowest precedence of runtime graphicssystem switches
if (graphics_system_name.isEmpty())
graphics_system_name = QString::fromLocal8Bit(qgetenv("QT_GRAPHICSSYSTEM"));
// Must be called before initialize()
//这里调用了qt_init
qt_init(this, qt_appType
#ifdef Q_WS_X11
, dpy, visual, cmap
#endif
);
initialize();
eventDispatcher->startingUp();

  1. ifdef QT_EVAL
    extern void qt_gui_eval_init(uint);
    qt_gui_eval_init(application_type);
    #endif
  1. if defined(Q_OS_SYMBIAN) && !defined(QT_NO_SYSTEMLOCALE)
    symbianInit();
    #endif
  1. ifndef QT_NO_LIBRARY
    if(load_testability) {
    QLibrary testLib(QLatin1String("qttestability"));
    if (testLib.load()) {
    typedef void (TasInitialize)(void);
    TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
    if (initFunction) {
    initFunction();
    } else {
    qCritical("Library qttestability resolve failed!");
    }
    } else {
    qCritical("Library qttestability load failed!");
    }
    }


//make sure the plugin is loaded
if (qt_is_gui_used)
qt_guiPlatformPlugin();
#endif
}

<br />这里也是初始化了很多资源其中要要说的是qt_init因为就是它创建了程序代码如下<br />

void qt_init(QApplicationPrivate /* priv */, int)
{
if (!CCoeEnv::Static()) {
// The S60 framework creates a new trap handler which will render any existing traps
// invalid as long as it is active. This means that all code in main() that occurs after
// the QApplication construction needs to be surrounded by a new trap, despite having
// an outer one already. To avoid this, we save the original trap handler here, and set
// it back after the S60 framework is constructed. Then we restore it right before the S60
// framework destruction.
TTrapHandler
origTrapHandler = User::TrapHandler();


// The S60 framework has not been initialized. We need to do it.
//这里就是创建程序的地方
TApaApplicationFactory factory(S60->s60ApplicationFactory ?
S60->s60ApplicationFactory : newS60Application);
CApaCommandLine commandLine = 0;
TInt err = CApaCommandLine::GetCommandLineFromProcessEnvironment(commandLine);
// After this construction, CEikonEnv will be available from CEikonEnv::Static().
// (much like our qApp).
CEikonEnv* coe = new CEikonEnv;
//not using QT_TRAP_THROWING, because coe owns the cleanupstack so it can't be pushed there.
if(err == KErrNone)
TRAP (err, coe->ConstructAppFromCommandLineL(factory,*commandLine)); //这里通过factory创建了程序
delete commandLine;
if(err != KErrNone) {
qWarning() << "qt_init: Eikon application construct failed ("
<< err
<< "), maybe missing resource file on S60 3.1?";
delete coe;
qt_symbian_throwIfError(err);
}

S60->s60InstalledTrapHandler = User::SetTrapHandler(origTrapHandler);

S60->qtOwnsS60Environment = true;
} else {
S60->qtOwnsS60Environment = false;
}

  1. ifdef QT_NO_DEBUG
    if (!qgetenv("QT_S60_AUTO_FLUSH_WSERV").isEmpty())
    #endif
    S60->wsSession().SetAutoFlush(ETrue);
  1. ifdef Q_SYMBIAN_WINDOW_SIZE_CACHE
    TRAP_IGNORE(S60->wsSession().EnableWindowSizeCacheL());
    #endif

S60->updateScreenSize();

TDisplayMode mode = S60->screenDevice()->DisplayMode();
S60->screenDepth = TDisplayModeUtils::NumDisplayModeBitsPerPixel(mode);

//NB: RWsSession::GetColorModeList tells you what window modes are supported,
//not what bitmap formats.
if(QSysInfo::symbianVersion() == QSysInfo::SV_9_2)
S60->supportsPremultipliedAlpha = 0;
else
S60->supportsPremultipliedAlpha = 1;

RProcess me;
TSecureId securId = me.SecureId();
S60->uid = securId.operator TUid();

// enable focus events - used to re-enable mouse after focus changed between mouse and non mouse app,
// and for dimming behind modal windows
S60->windowGroup().EnableFocusChangeEvents();

//Check if mouse interaction is supported (either EMouse=1 in the HAL, or EMachineUID is one of the phones known to support this)
const TInt KMachineUidSamsungI8510 = 0x2000C51E;
// HAL::Get(HALData::EPen, TInt&amp; result) may set 'result' to 1 on some 3.1 systems (e.g. N95).
// But we know that S60 systems below 5.0 did not support touch.
static const bool touchIsUnsupportedOnSystem =
QSysInfo::s60Version() QSysInfo::SV_S60_3_1

       || QSysInfo::s60Version()  QSysInfo::SV_S60_3_2;
TInt machineUID;
TInt mouse;
TInt touch;
TInt err;
err = HAL::Get(HALData::EMouse, mouse);
if (err != KErrNone)
mouse = 0;
err = HAL::Get(HALData::EMachineUid, machineUID);
if (err != KErrNone)
machineUID = 0;
err = HAL::Get(HALData::EPen, touch);
if (err != KErrNone || touchIsUnsupportedOnSystem)
touch = 0;
#ifdef WINS
if(QSysInfo::symbianVersion() <= QSysInfo::SV_9_4) {
//for symbian SDK emulator, force values to match typical devices.
mouse = 0;
touch = touchIsUnsupportedOnSystem ? 0 : 1;
}
#endif
if (mouse || machineUID == KMachineUidSamsungI8510) {
S60->hasTouchscreen = false;
S60->virtualMouseRequired = false;
}
else if (!touch) {
S60->hasTouchscreen = false;
S60->virtualMouseRequired = true;
}
else {
S60->hasTouchscreen = true;
S60->virtualMouseRequired = false;
}

S60->avkonComponentsSupportTransparency = false;
S60->menuBeingConstructed = false;

  1. ifdef Q_WS_S60
    TUid KCRUidAvkon = { 0x101F876E };
    TUint32 KAknAvkonTransparencyEnabled = 0x0000000D;

CRepository* repository = 0;
TRAP (err, repository = CRepository::NewL(KCRUidAvkon));

if(err KErrNone) {

       TInt value = 0;
       err = repository->Get(KAknAvkonTransparencyEnabled, value);
       if(err  KErrNone) {
S60->avkonComponentsSupportTransparency = (value==1) ? true : false;
}
}
delete repository;
repository = 0;
#endif
  1. ifdef QT_KEYPAD_NAVIGATION
    if (touch) {
    QApplicationPrivate::navigationMode = Qt::NavigationModeNone;
    } else {
    QApplicationPrivate::navigationMode = Qt::NavigationModeKeypadDirectional;
    }
    #endif
  1. ifndef QT_NO_CURSOR
    //Check if window server pointer cursors are supported or not
    #ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
    //In generic binary, use the HAL and OS version
    //Any other known good phones should be added here.
    if (machineUID == KMachineUidSamsungI8510 || (QSysInfo::symbianVersion() != QSysInfo::SV_9_4
    && QSysInfo::symbianVersion() != QSysInfo::SV_9_3 && QSysInfo::symbianVersion()
     != QSysInfo::SV_9_2)) {
    S60->brokenPointerCursors = false;
    qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup());
    }
    else
    S60->brokenPointerCursors = true;
    #endif

if (S60->mouseInteractionEnabled) {
#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
if (S60->brokenPointerCursors) {
qt_symbian_set_pointer_sprite(Qt::ArrowCursor);
qt_symbian_show_pointer_sprite();
}
else
#endif
S60->wsSession().SetPointerCursorMode(EPointerCursorNormal);
}
#endif

QFont systemFont;
systemFont.setFamily(systemFont.defaultFamily());
QApplicationPrivate::setSystemFont(systemFont);

  1. ifdef SYMBIAN_GRAPHICS_WSERV_QT_EFFECTS
    QObject::connect(qApp, SIGNAL (aboutToQuit()), qApp, SLOT (_q_aboutToQuit()));
    #endif

/*
### Commented out for now as parameter handling not needed in SOS (yet). Code below will break testlib with -o flag
int argc = priv->argc;
char **argv = priv->argv;

// Get command line params
int j = argc ? 1 : 0;
for (int i=1; i<argc; i+) {
if (argv[i] && *argv[i] != '-') {
argv[j
+] = argv[i];
continue;
}

  1. if defined(QT_DEBUG)
    if (qstrcmp(argv[i], "-nograb") == 0)
    appNoGrab = !appNoGrab;
    else
    #endif // QT_DEBUG
     ;
    }
    */


// Register WId with the metatype system. This is to enable
// QWidgetPrivate::create_sys to used delayed slot invocation in order
// to destroy WId objects during reparenting.
qRegisterMetaType<WId>("WId");
}

<br />代码很多哈哈上面代码中标有注释这里就是创建程序的地方 的部分:<br />

TApaApplicationFactory factory(S60->s60ApplicationFactory ?
S60->s60ApplicationFactory : newS60Application);

<br />这里将newS60Application赋值给S60->s60ApplicationFactory而newS60Application是什么呢我们看看它的定义<br />

CApaApplication newS60Application()
{
return new QS60MainApplication;
}

<br />由此可知newS60Application就是一个函数指针它创建了一个QS60MainApplication实例这里QS60MainApplication类封装了CAknApplication的实现<br />另外上面代码中s60是一个全局数据里面放了很多东西主要是方便直接查询其类结构如下<br />

class QS60Data
{
public:
QS60Data();
QThreadStorage<QS60ThreadLocalData
> tls;
TUid uid;
int screenDepth;
QPoint lastCursorPos;
QPoint lastPointerEventPos;
QPointer<QWidget> lastPointerEventTarget;
QPointer<QWidget> mousePressTarget;
int screenWidthInPixels;
int screenHeightInPixels;
int screenWidthInTwips;
int screenHeightInTwips;
int defaultDpiX;
int defaultDpiY;
WId curWin;
enum PressedKeys {
Select = 0x1,
Right = 0x2,
Down = 0x4,
Left = 0x8,
Up = 0x10,
LeftUp = 0x20,
RightUp = 0x40,
RightDown = 0x80,
LeftDown = 0x100
};
int virtualMousePressedKeys; // of the above type, but avoids casting problems
int virtualMouseAccelDX;
int virtualMouseAccelDY;
QElapsedTimer virtualMouseAccelTimeout;
int virtualMouseMaxAccel;
#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS
int brokenPointerCursors : 1;
#endif
int hasTouchscreen : 1;
int mouseInteractionEnabled : 1;
int virtualMouseRequired : 1;
int qtOwnsS60Environment : 1;
int supportsPremultipliedAlpha : 1;
int avkonComponentsSupportTransparency : 1;
int menuBeingConstructed : 1;
QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type

enum ScanCodeState {
Unpressed,
KeyDown,
KeyDownAndKey
};
QHash<TInt, ScanCodeState> scanCodeStates;

static inline void updateScreenSize();
inline RWsSession&amp; wsSession();
static inline RWindowGroup&amp; windowGroup();
inline CWsScreenDevice* screenDevice();
static inline CCoeAppUi* appUi();
static inline CEikMenuBar* menuBar();
#ifdef Q_WS_S60
static inline CEikStatusPane* statusPane();
static inline CCoeControl* statusPaneSubPane(TInt aPaneId);
static inline CAknTitlePane* titlePane();
static inline CAknContextPane* contextPane();
static inline CEikButtonGroupContainer* buttonGroupContainer();
static void setStatusPaneAndButtonGroupVisibility(bool statusPaneVisible, bool buttonGroupVisible);
#endif
static void controlVisibilityChanged(CCoeControl *control, bool visible);

  1. ifdef Q_OS_SYMBIAN
    TTrapHandler *s60InstalledTrapHandler;
    #endif
    };