Tux

Kylix Tips From Under The Hood

The Kylix source code reveals many riches that bypass the casual developer who restricts themselves to the help files. This page is dedicated to sniffing out these little gems deposited by the R&D people at Borland. You will also find references to useful sites that also give good information.

The hope is that as more people buy Kylix, and it becomes a more entrenched development tool, this will become a good reference point for those who need a little more than the documentation provides.

Note that most questions are to do with the Delphi version of Kylix (the only version in Kylix 1 and 2). As time passes I hope to update them to cover both the Delphi and C++ versions of Kylix (shipped with Kylix 3 onwards).

Contents

What Conditional Symbols Are Defined In Kylix?
What Configuration Files Does Kylix Use?
Cannot Register Kylix Open Edition
Can I Use The Electric Fence Debugging Library In A Kylix Application?
How Can I Get Kylix To Notice Function Key Shortcuts, Such As Ctrl+F2?
How Can I Remove The Kylix Open Edition Banner?
How Can I Keep An Exception Object Alive After a try/except/end Statement?
How Do VisualCLX Components Work On The Inside?
What Is The CLXDisplay API?
What Are Hook Objects?
How Do I Pick Up The Equivalent Of Windows Messages?
How Do I Trap A Qt Widget Signal?
Can I Use Message Handler Methods in Kylix To Pick Up Qt Events?
Can I Write A Window Manager For X Windows In Kylix?
Kylix Easter Eggs
Runtime Error 230 Starting Kylix
Error -10 From The Installation Program
The font matrix generation dialog hangs when I run Kylix for the first time


What Conditional Symbols Are Defined In Kylix?

Kylix defines a number of conditional symbols for use in conjunction with conditional compilation directives. However, to put them into context the following table lists all the conditional symbols defined in all versions of Kylix, Delphi and the Delphi compiler incorporated in C++Builder.

Conditional Symbol Meaning Defined In
CPU386 compiling on an 80386 or better all versions of Kylix and Delphi
VER80 compiler version 8 Delphi 1
VER90 compiler version 9 Delphi 2
VER93 compiler version 9.3 C++Builder 1
VER100 compiler version 10 Delphi 3
VER110 compiler version 11 C++Builder 3
VER120 compiler version 12 Delphi 4
VER125 compiler version 12.5 C++Builder 4
VER130 compiler version 13 Delphi/C++Builder 5
VER140 compiler version 14 Delphi/C++Builder 6, Kylix 1, 2 & 3
VER150 compiler version 15 Delphi 7
VER160 compiler version 16 Delphi for .NET
BCB Delphi code being compiled from within C++Builder C++Builder
GAMMA_TEST Delphi code being compiled from a pre-release (gamma test) product C++Builder 4 (this was a bug and should not be defined in any shipping product)
WINDOWS compiling for Win16 platform Delphi 1
WIN32 compiling for Win32 platform 32-bit Delphi versions from 2 onwards
MSWINDOWS compiling for any Windows platform Delphi 6 and later
LINUX compiling for any Linux platform Kylix
LINUX32 compiling for 32-bit Linux platform Kylix
POSIX compiling for a POSIX-compliant OS Kylix
POSIX32 compiling for a 32-bit POSIX-compliant OS Kylix
PIC position independent code (PIC) being generated Kylix when compiling shared objects
ELF compiling an ELF format executable file Kylix
PC_MAPPED_EXCEPTIONS program counter based exception handling, as opposed to OS-based exception handling Kylix
DECLARE_GPL compiling a GPL application Kylix Open Edition
CLR compiling for the Common Language Runtime (Microsoft .NET platform) Delphi for .NET
CIL Common Intermediate Language (CIL) code being generated Delphi for .NET
MANAGEDCODE .NET managed code being generated Delphi for .NET
CONDITIONALEXPRESSIONS conditional directives support expression evaluation Delphi/C++Builder 6 and later, Delphi for .NET and Kylix

With these you can write conditionally compiled code like:

{$IFDEF LINUX}
  //do Linux-specific code here
{$ENDIF}
{$IFDEF MSWINDOWS}
  //do WIndows-specific code here
{$ENDIF}

However, as of Kylix 1 and Delphi 6 you can also take advantage of some new constants defined in the RTL in conjunction with a more flexible form of consitional compilation. The following table shows the new predefined constants that you may want to use.

Product RTLVersion value CompilerVersion value
Kylix 1 14.00 not defined
Delphi 6.00 14.10 14.01
Delphi 6.01 14.11 14.01
Delphi 6.02 14.20 14.01
C++Builder 6 14.20 14.01
Kylix 2 14.20 14.10
Kylix 3 14.50 14.50
Delphi 7 15.00 15.00
Delphi for .NET Preview Edition not defined 16.00

You can use these constants to more easily identify if the compiler is later or earlier than a specific release. You do this with the help of the intrinsic Declared function, which identifies if a specified constant has been declared. For example:

{$IFDEF CONDITIONALEXPRESSIONS}
  {$IF Declared(RTLVersion) and RTLVersion < 14.11}
    {$MESSAGE ERROR 'Oldest permitted compilers are Delphi 6 with Update Pack 1 (Delphi 6.01) or Kylix 2'}
  {$IFEND}
{$ENDIF}

If you know you will always be compiling with tools later than Delphi 5 (i.e. Delphi 6 or Kylix) then there is no need to wrap these new directives in a check for CONDITIONALEXPRESSIONS. Also, you can you can use another instrisic function, Defined, which checks if a specified conditional symbol has been defined (much like $IFDEF does):

{$IF not Defined(LINUX)}
  {$MESSAGE ERROR 'This is Linux-only code'}
{$ELSEIF Declared(RTLVersion) and RTLVersion < 14.20}
  {$MESSAGE ERROR 'Oldest permitted compiler is Kylix 2'}
{$IFEND}

Click here to go back to the top of this page


What Configuration Files Does Kylix Use?

Assuming Kylix has been installed by user root it will use a number of configuration files as listed below. These are default configuration files, used when the Kylix user does not have any local override files.

Kylix 1 Global Configuration Files

Configuration filePurpose
/usr/local/etc/borlandrc.confWINE configuration file
/usr/local/etc/dbxconnections.confdbExpress connection configuration
/usr/local/etc/dbxdrivers.confdbExpress driver configuration
/usr/local/etc/dcc.confDefault command-line compiler options
/usr/local/etc/delphi60dci.confDefault code templates invoked with Ctrl+J
/usr/local/etc/delphi60dmt.confDefault menu templates used in the Menu Designer
/usr/local/etc/delphi60dro.confObject Repository setup
/usr/local/etc/delphi60rc.confGeneral Kylix information, such as root installation directory, IDE packages, debug DCU path, help file location, browsing paths

Kylix 2 Global Configuration Files

Configuration filePurpose
/usr/local/etc/borlandrc.confWINE configuration file
/usr/local/etc/dbxconnections.confdbExpress connection configuration
/usr/local/etc/dbxdrivers.confdbExpress driver configuration
/usr/local/etc/dcc.confDefault command-line compiler options
/usr/local/etc/delphi65dci.confDefault code templates invoked with Ctrl+J
/usr/local/etc/delphi65dmt.confDefault menu templates used in the Menu Designer
/usr/local/etc/delphi65dro.confObject Repository setup
/usr/local/etc/delphi650rc.confGeneral Kylix information, such as root installation directory, IDE packages, debug DCU path, help file location, browsing paths

Kylix 3 Global Configuration Files

The files beginning with delphi are specific to the Delphi version of Kylix and those beginning with bcb are specific to the C++ version of Kylix.

Configuration filePurpose
/usr/local/etc/bcb69dci.confDefault code templates invoked with Ctrl+J
/usr/local/etc/bcb69dmt.confDefault menu templates used in the Menu Designer
/usr/local/etc/bcb69dro.confObject Repository setup
/usr/local/etc/bcb69rc.confGeneral Kylix information, such as root installation directory, IDE packages, help file location, browsing paths
/usr/local/etc/bccrcDefault C++ command-line compiler options
/usr/local/etc/borlandrc.confWINE configuration file
/usr/local/etc/dbxconnections.confdbExpress connection configuration
/usr/local/etc/dbxdrivers.confdbExpress driver configuration
/usr/local/etc/dcc.confDefault Delphi command-line compiler options
/usr/local/etc/debug69rc.confIntegrated debugger configuration
/usr/local/etc/delphi69dci.confDefault code templates invoked with Ctrl+J
/usr/local/etc/delphi69dmt.confDefault menu templates used in the Menu Designer
/usr/local/etc/delphi69dro.confObject Repository setup
/usr/local/etc/delphi69rc.confGeneral Kylix information, such as root installation directory, IDE packages, debug DCU path, help file location, browsing paths
/usr/local/etc/delphi69upg.confConfiuration file for IDE to auto-update package info from older Kylix versions
/usr/local/etc/ilinkrcDefault C++ command-line linker options
/usr/local/etc/incfilesdat.confList of C++ include files

The WINE file might warrant some explanation. The Kylix IDE carries much of its old Windows-specific code inside. To avoid rewriting the whole thing from scratch in Linux with CLX, Borland chose to compile parts of the WINE Windows emulation library into the IDE to allow the Windows/VCL code to compile into a native Linux ELF (Executable and Linking Format) executable. This meant the IDE could be made available in a much shorter time than if it was re-written in CLX.

The files above can be considered to contain the global options. When a user first runs Kylix, a .borland directory is made in their home directory (~/.borland). This is a hidden directory, thanks to the first character being a full stop, but you can see hidden files at the prompt by running:

ls -a

In this directory are local configuration files which, in many cases, override the global defaults (listed below). It would appear that when Kylix starts, it checks whether the ~/.borland directory exists. If it doesn't, it creates it and copies over the original configuration files so the user's specific options do not interfere with anyone else's.

Kylix 1 Local Configuration Files

Configuration filePurpose
~/.borland/borl~wex.conUser-specific version of borlandrc.conf
~/.borland/cachedmetrics.:0WINE font metrics (the final part of the name is based on your X display and defaults to 0)
~/.borland/dbxconnectionsUser-specific dbExpress connection configuration
~/.borland/dbxdriversUser-specific dbExpress driver configuration
~/.borland/defproj.confUser-specific default command-line compiler options
~/.borland/defproj.kofUser-specific default IDE project options
~/.borland/delphi60rcUser-specific version of delphi60rc.conf
~/.borland/delphi60dciUser-specific code templates
~/.borland/delphi60dmtUser-specific menu templates
~/.borland/delphi60droUser-specific Object Repository setup
~/.borland/delphi.dctUser's component templates
~/.borland/wineserver-hostname:0Directory for WINE usage (the machine name and X display are encoded into this directory name)

Kylix 2 Local Configuration Files

Configuration filePurpose
~/.borland/borlandrc.confUser-specific version of borlandrc.conf
~/.borland/cachedmetrics.:0WINE font metrics
~/.borland/dbxconnectionsUser-specific dbExpress connection configuration
~/.borland/dbxdriversUser-specific dbExpress driver configuration
~/.borland/defproj.confUser-specific default command-line compiler options
~/.borland/defproj.kofUser-specific default IDE project options
~/.borland/delphi65rcUser-specific version of delphi65rc.conf
~/.borland/delphi65dciUser-specific code templates
~/.borland/delphi65dmtUser-specific menu templates
~/.borland/delphi65droUser-specific Object Repository setup
~/.borland/delphi.dctUser's component templates
~/.borland/wineserver-hostname:0Directory for WINE usage

Kylix 3 Local Configuration Files

The files beginning with delphi are specific to the Delphi version of Kylix and those beginning with bcb are specific to the C++ version of Kylix.

Configuration filePurpose
~/.borland/bcb69rcUser-specific version of bcb69rc.conf
~/.borland/bcb69dciUser-specific code templates
~/.borland/bcb69dmtUser-specific menu templates
~/.borland/bcb69droUser-specific Object Repository setup
~/.borland/bcb.dctUser's component templates
~/.borland/borland.licUser-specific registration information
~/.borland/borlandrc.confUser-specific version of borlandrc.conf
~/.borland/cachedmetrics.:0WINE font metrics
~/.borland/clx69.csmDefault C++ precompiled header file
~/.borland/dbxconnectionsUser-specific dbExpress connection configuration
~/.borland/dbxdriversUser-specific dbExpress driver configuration
~/.borland/debug69rcUser-specific version of debug69rc.conf
~/.borland/default.bprUser-specific default C++ IDE project options
~/.borland/defproj.confUser-specific default Delphi command-line compiler options
~/.borland/defproj.kofUser-specific default Delphi IDE project options
~/.borland/delphi69rcUser-specific version of delphi69rc.conf
~/.borland/delphi69dciUser-specific code templates
~/.borland/delphi69dmtUser-specific menu templates
~/.borland/delphi69droUser-specific Object Repository setup
~/.borland/delphi.dctUser's component templates
~/.borland/wineserver-hostname:0Directory for WINE usage
~/.borland/incfiles.datUser-specific version of incfilesdat.conf
~/.borland/registry.datUser-specific registration information
~/.borland/registry.slmUser-specific registration information (binary)

Click here to go back to the top of this page


Cannot Register Kylix Open Edition

This seems to be upsetting a number of people. Kylix asks you to register it, so you go to the trouble of contacting Borland to get the relevant information to enter. After entering, Kylix says you need to enter registration information and refuses to run.

If you installed Kylix as root then you should try removing ~/.borland directory and start Kylix again. When it asks you to register again enter the same information you tried to enter before.

If you installed Kylix as a normal user, delete ~/.borland/registry.bin. Now start Kylix and again enter the registration information.

In both these cases you should be able to get past the previous recursive loop. In fact you should also be able to defer registration to some later point if you wish and have Kylix start in an unregistered form.

You can find more information on this subject in an article Borland's Community Web site.

Click here to go back to the top of this page


Can I Use The Electric Fence Debugging Library In A Kylix Application?

The Electric Fence library is a great tool for track down memory problems in your application as they occur. It replaces the default Linux memory manager with one that uses hardware protection to perform stringent checking of your application's memory use, flagging any problems relating to memory buffer overruns or underruns, for example.

The Kylix developers thought of Electric Fence whilst working on the RTL (Run-Time Library) and included some conditional compilation code that links it in.

The first thing you'll need is a copy of Electric Fence which you can get from here.

Then you'll need to recompile the RTL source code with the EFENCE conditional symbol defined. This is straightforward as Borland provide a GNU makefile in Kylix's source/rtl directory, set up to recompile the RTL in a manner suitable for writing GPL applications. Navigate to the directory, ensure you have rights in the Kylix directory tree and execute these statements:

export DCCSYSSWTS=-DEFENCE
make debug

This recompiles the RTL with the EFENCE conditional symbol defined, changing the memory management code link to Electric Fence. The new RTL units will be under the Kylix root directory in units/debug, so make sure you add $(Delphi)/units/debug to the beginning of your unit search path in the Directories/Conditionals page of the project options dialog (choose Project | Options... or press Shift+Ctrl+F11).

When a memory error occurs, Electric Fence should now alert you.

Click here to go back to the top of this page


How Can I Get Kylix To Notice Function Key Shortcuts, Such As Ctrl+F2?

When you press function keys, in association with Ctrl or Shift (and there are quite a few shortcuts in Kylix that use these keys), the Window Manager has a tendency to pick them up first, assuming the keystroke is for them. This makes sense if you think about it, as in KDE, Ctrl+F2 takes you to the second virtual screen of your X session and Ctrl+Tab takes you to the next virtual screen.

Fortunately, you can usually hide the keystrokes from the Windows Manager by turning on the Scroll Lock key. Window Managers seem to check that Scroll Lock is off before stealing the keystrokes, so turning it on allows Kylix to pick them. Just remember to turn Scroll Lock off when you close Kylix, or switch away from it, to allow things to get back to normal.

Click here to go back to the top of this page


How Can I Remove The Kylix Open Edition Banner?

Kylix Open Edition GPL applications like to adevertise the product used to create them. GUI (X windows) applications display a stay-on-top window for three seconds.

The good new is that clicking on the window as soon as it appears makes it go away. The even better news is that you can pass the -ns command-line switch to your application (anywhere on the command-line) and it won't display it at all.

On a similar note, Kylix console applications write a line or two of text to stdout, as soon as they startup, mentioning that the application is GPL and was developed in Kylix. This occurs due to the occurrence of the {$APPTYPE CONSOLE} directive in the project source file.

If you remove this directive, the "nag" lines will disappear, but you will still be able to write a normal console application, as all Kylix applications have access to the console.

Click here to go back to the top of this page


How Can I Keep An Exception Object Alive After a try/except/end Statement?

When an exception is raised, the exception object is destroyed automatically when the except clause, which handles the exception, is exited. There are some cases in which an application may wish to acquire the thrown object and keep it alive after the except clause is exited. For this purpose, Borland have added the AcquireExceptionObject and ReleaseExceptionObject functions.

These functions maintain a reference count on the most current exception object, allowing applications to legitimately obtain references. AcquireExceptionObject increments an exception object's reference count and ReleaseExceptionObject decrements it.

If the reference count for an exception that is being thrown is positive when the except clause is exited, then the thrown object is not destroyed by the RTL, but assumed to be in control of the application. It is then the application's responsibility to destroy the thrown object. If the reference count is zero, then the RTL will destroy the thrown object when the except clause is exited.

Click here to go back to the top of this page


How Do VisualCLX Components Work On The Inside?

VisualCLX is the part of CLX that represents the visual components that would reside in the TWinControl hierarchy in the VCL.

The VCL makes use of natively implemented Windows controls and provides classes that turn those controls into components. Any component representing a control that is implemented by Windows (and therefore has a window handle) will be inherited at some point from TWinControl. However, this approach only works where Windows controls exist, which means the VCL only works on Windows platforms.

The VisualCLX framework is a set of classes that represent visual controls but must work where possible on both Microsoft Windows and in X Windows in Linux (these are the currently supported platforms, where both must be running on Intel-compatible chips). The controls represented by the VisualCLX components are implemented by a C++ class library called Qt (produced cute), from a Norwegian development company called Trolltech.

The Qt library has a strong presence on Linux, where (for example) the people who develop the popular KDE desktop environment use it. Qt implements a number of controls for X Windows applications, many of which mirror those available in Windows (X Windows implements no controls of its own). Qt is also available on Windows where the same classes allow C++ applications to be ported between Linux and Windows reasonably easily.

Qt is an example of a control library, one of many that exist (others include GTK+ and Athena). These control libraries are generally referred to as widget libraries, where a widget is the common Linux term for a control (from a contraction of window gadget).

All controls in VisualCLX are made from Qt widgets, and consequently, the equivalent of the VCL TWinControl class is called TWidgetControl (although the type TWinControl is defined to be the same as TWidgetControl).

Qt is a C++ class library and, because of differences in C++ and ObjectPascal implementation details, an ObjectPascal program cannot directly manipulate Qt widgets. Instead, VisualCLX makes use of an additional library, typically referred to as the Qt interface library. This library is also written in C++, but it exports all the Qt functionality in a manner that is accessible to ObjectPascal code.

The import unit for this interface library is called Qt.pas and is referred to as the CLXDisplay API. In this unit you can find all the Qt functionality from the original C++ classes exposed as flat methods (a method of a class that is declared as a standalone subroutine). This means that rather than being declared as classes, the Qt widget methods are all imported as functions. However, since at the C++ side they are indeed classes, almost every flat method takes one extra parameter, which is the reference to the Qt widget.

In a normal ObjectPascal application, you call methods via object references, for example:

Button1.SetBounds(10, 10, 75, 25);

When a normal method is turned into a flat method, the object reference is passed as the first parameter so the method code knows which instance it should be working against. Here is a fictitious example flat method, which is equivalent to the method just used:

TButton_SetBounds(Button1, 10, 10, 75, 25);

In the made-up example shown here, there is little point using the flat method, as an object reference like Button1 always allows you access to its members. In the real case of the CLXDisplay API however, the object references cannot be used in this way (which is why the flat methods are provided in the first place).

Any Qt constructor (which, again, is declared as a flat method) returns a reference to the C++ Qt widget, but this cannot be used as such in a CLX application. The flat constructor returns a pointer (which is what an object reference is), which is usable as the first parameter to appropriate flat methods, but not for direct access to the pertinent methods. Such a pointer is referred to as an opaque reference to a Qt widget.

Rather than leave these opaque references as raw pointers, CLX defines an ObjectPascal class hierarchy to mirror the Qt hierarchy, but which defines no methods or properties at all for any of its classes. These classes are representative only. They allow what would otherwise be raw pointers to be defined of appropriate types in a hierarchy, and thereby allow the compiler to validate the use of these values. The flat constructors return values defined in terms of classes from this hierarchy.

Again, the returned value is of little use on its own, but is useful when passed to an appropriate flat method. It is quite common for the term handle to be used for a value that has a use in an application, but has no real meaning to the programmer. Take for example a window handle, or a file handle; these mean nothing to the programmer, other than a means to uniquely identify a given window or a given open file. Opaque Qt references, when defined in terms of classes in the aforementioned ObjectPascal class hierarchy (a portion of which can be seen below) are referred to as Qt widget handles, or simply Qt handles for similar reasons.

type
  QtH = class(TObject) end;
    QObjectH = class(QtH) end;
      QApplicationH = class(QObjectH) end;
        QClxApplicationH = class(QApplicationH) end;
      QWidgetH = class(QObjectH) end;
        QOpenWidgetH = class(QWidgetH);
        QButtonH = class(QWidgetH) end;
          QPushButtonH = class(QButtonH) end;
            QClxBitBtnH = class(QPushButtonH) end;
        QComboBoxH = class(QWidgetH) end;
          QOpenComboBoxH = class(QComboBoxH) end;
      QFrameH = class(QWidgetH) end;
        QTableViewH = class(QFrameH) end;
          QMultiLineEditH = class(QTableViewH) end;

In the VCL, a visual component's Handle property is a window handle. In a VisualCLX component, the Handle property is a Qt widget handle.

You can see that the flat methods make use of these handle types by looking at some of their declarations (some of the flat methods of QButton are listed below). The flat constructor for the QPushButton widget returns a QPushButton handle (defined as type QPushButtonH). All the other flat methods require a QPushButton handle as their first argument.

function QPushButton_create(parent: QWidgetH; name: PAnsiChar): QPushButtonH; overload; cdecl;
function QPushButton_create(text: PWideString; parent: QWidgetH; name: PAnsiChar): QPushButtonH; overload; cdecl;
procedure QPushButton_destroy(handle: QPushButtonH); cdecl;
procedure QPushButton_setGeometry(handle: QPushButtonH; x: Integer; y: Integer; w: Integer; h: Integer); overload; cdecl;
procedure QPushButton_setGeometry(handle: QPushButtonH; p1: PRect); overload; cdecl;

The following listing shows how you can create, manipulate and destroy a Qt widget solely using Qt handles and Qt flat methods. Notice that the QPushButton constructor is called, followed by a QPushButton method. The next call is to a QButton method that QPushButton inherits and the last call is to the QPushButton destructor.

uses
  Qt, QTypes;
...
var
  Btn: QPushButtonH;
...
procedure TForm1.FormCreate(Sender: TObject);
var
  Msg: TCaption;
begin
  Btn := QPushButton_create(Handle, PChar('Btn'));
  QPushButton_setGeometry(Btn, 10, 10, 75, 25);
  Msg := 'Press me';
  QButton_setText(Btn, PWideString(@Msg));
 end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  QPushButton_destroy(Btn);
end;

Of course you would normally have no need to write code like this as TButton does it all for you, but it serves as a simplified example of how CLX components do their thing by using the CLXDisplay API.

Now that we have a general idea of how the Qt widgets are manipulated, it might be an idea to look at the various methods involved in creating and destroying the underlying control used by a component. Some of the key methods in the life and death of a VCL windowed control (TWinControl descendant) are different in a VisualCLX component.

When a VCL control is displayed, a window handle is needed and so the virtual CreateHandle method is called. CreateHandle calls the virtual CreateWnd method to get a window handle and then sets the control's z-order.

CreateWnd sets things up for the Windows API call that will ultimately be called. It sets up the window creation parameters through another virtual method, CreateParams, which itself may well call CreateSubClass to make use of an existing Windows control class.

After CreateParams returns and various things have been checked, the virtual CreateWindowHandle method is called to finish off the job. CreateWindowHandle calls the Windows API CreateWindowEx to create the underlying control and assigns the resultant window handle to the Handle property.

At the other end of the control's life, when it needs to be disposed of, the DestroyHandle virtual method is called. This calls DestroyHandle for any child controls and then calls the virtual DestroyWnd method. DestroyWnd is intended to destroy the underlying windowed control and it does so by calling the virtual DestroyWindowHandle method. DestroyWindowHandle finishes the job by calling the Windows API DestroyWindow.

In CLX, the corresponding TWidgetControl class also has a chain of virtual methods responsible for setting up and pulling down an underlying Qt widget. When the control is required, the CreateHandle method is called, however unlike in the VCL, CreateHandle is not virtual. CreateHandle calls the virtual CreateWidget method first, which creates the underlying widget, storing the widget handle in the Handle property. It then creates an appropriate hook object for the widget and assigns its handle to the Hooks property, so that the component can react to interesting things that happen to the widget. For more information about hook objects, see the appropriate question on this page.

After CreateWidget, CreateHandle then calls the virtual InitWidget, which initialises the freshly created widget settings. It then goes on to call HookEvents, which uses the hook object to connect ObjectPascal methods to the widget signals. For more information on hooking widget signals, see the appropriate question on this page.

When the widget is no longer required the non-virtual DestroyHandle method is called. This calls the virtual DestroyWidget which firstly calls the dynamic WidgetDestroyed method (destroys the hook object) and then proceeds to destroy the widget itself. The table below summarises all these calls.

 VCLCLX
Control/widget creation CreateHandle
  CreateWnd
    CreateParams
      CreateSubClass
    CreateWindowHandle
      CreateWindowEx
CreateHandle
  CreateWidget
    
widget destructor
  InitWidget
  HookEvents

Control/widget destruction DestroyHandle
  DestroyWnd
    DestroyWindowHandle
      DestroyWindow
DestroyHandle
  DestroyWidget
    WidgetDestroyed
    
widget destructor

Click here to go back to the top of this page


What Is The CLXDisplay API?

The CLXDisplay API is the formal name for the Qt.pas unit that ships with Kylix and also with Delphi 6 and later. It acts as an import unit for the Qt widget library used by VisualCLX. All visual controls in CLX are based around Qt widgets (Qt control), and they are manipulated using routines imported via Qt.pas.

In truth, things are a little more complicated than this. Qt is a C++ class library, and ObjectPascal cannot readily manipulate C++ classes. Because of this, Borland wrote an additional library to sit between a CLX application and the Qt library. This extra library is called libqtintf.so (the Qt interface library), and Qt.pas is actually an import unit for this interface library.

You can read more about the CLXDisplay API in other questions on this page, and also in:

  1. How CLX Uses Qt by Brian Long, The Delphi Magazine, Issue 70, June 2001.
    This article looks under the hood of CLX and finds out how the Qt C++ class library is used from ObjectPascal. It introduces all the key component areas where Qt entities are manipulated and explains the mechanics of how the CLXDisplay API (the Qt.pas unit) allows an ObjectPascal program to talk to C++ classes.
  2. Programming Kylix with the CLXDisplay API by Bruno Sonnino, http://community.borland.com/article/0,1410,27231,00.html, April 2001.
    This article shows how to talk to Qt from CLX applications using the CLXDisplay API (the Qt.pas unit).

Click here to go back to the top of this page


What Are Hook Objects?

A hook object is a simple C++ object that exists in the Qt interface library. Its purpose is to act as an intermediary between Object Pascal code in a Kylix application and C++ widgets from the Qt library.

In order to customise the behaviour of a widget in a Qt application, you can respond to a widget "signal" (like an event) by implementing an appropriate "slot" (like an event handler). However, it is not possible to have the slot written directly in Object Pascal. So the Qt interface library defines a hook class for each widget class. The hook class implements a simple slot for each available widget signal, whose sole job is to call some code in your Kylix application.

A hook object therefore acts as a bridge between a C++ widget and an Object Pascal CLX component.

Another form of customisation supported by widgets is the installation of an event filter, which is able to filter the OS events that get to the widget in the first place. Again, an Object Pascal method is not a suitable event filter, so each hook object installs a simple event filter into the associated widget. This event filter's sole job is to call a supplied Kylix method which acts as an Object Pascal event filter.

Click here to go back to the top of this page


How Do I Pick Up The Equivalent Of Windows Messages?

The Qt library represents basic low-level sysstem information, such as keystrokes and mouse clicks, as Qt events. If Qt is running on Microsoft Windows, Qt events are representations of the low level Windows messages. If Qt is running on X Windows, Qt events are representations of X events. In order to intercept these Qt events, and potentially tailor the handling of them, you need an event filter.

All CLX components based on Qt widgets (components inherited from TWidgetControl) have a virtual method called EventFilter, which is installed in the corresponding Qt widget by the component's hook object. In any descendant class, you would declare the method as:

function EventFilter(Sender: QObjectH; Event: QEventH): Boolean; override;

The event object is accessible through the Event widget handle. The CLXDisplay API unit declares the available Qt event object handle types and the corresponding flat methods that they support.

A return value of False allows the event to continue its journey to the widget, whereas a return value of True indicated the event has been handled and should not be delivered to the widget. Note that you should typically call the inherited version of EventFilter in your overridden version.

This is the event filter as implemented in the radio group component. You can see that is checks for a Show event, and also a Key Press event that indicates the Tab key has been pressed:

function TCustomRadioGroup.EventFilter(Sender: QObjectH; Event: QEventH): Boolean;
begin
  try
    if (QEvent_type(Event) = QEventType_show) then
      ForceLayout;
    if (QEvent_type(Event) = QEventType_KeyPress) and
    (QKeyEvent_key(QKeyEventH(Event)) = Key_Tab) then
      Result := False
    else
      Result := inherited EventFilter(Sender, Event);
  except
    Application.HandleException(Self);
    Result := False;
  end;
end;

Click here to go back to the top of this page


How Do I Trap A Qt Widget Signal?

The Qt documentation describes various signals that may be emitted by each different widget. Sometimes you may need to customise a widget's response to a given signal in your component class. To do this requires using your TWidgetControl descendant's hook object.

Firstly, you must declare a method in your class that matches the signal signature, as described in the Qt documentation. For example, when a widget is destroyed, it issues a destroyed signal, defined as follows int he Qt docs:

void QObject::destroyed()

A Kylix method that matches this description would be as follows (not the C calling convention is specified):

procedure DestroyedHook; cdecl;

This method must be passed along to the hook object in your component's HookEvents method. The following listing shows how the memo component hooks the textChanged and returnPressed signals. Notice that the corresponding hook object has appropriate methods for setting up CLX versions of slots for all appropriate signals that are supported by the widget.

procedure TCustomMemo.HookEvents;
var
  Method: TMethod;
begin
  inherited;
  QMultiLineEdit_textChanged_Event(Method) := TextChangedHook;
  QMultiLineEdit_hook_hook_textChanged(QMultiLineEdit_hookH(Hooks), Method);
  QMultiLineEdit_returnPressed_Event(Method) := ReturnPressedHook;
  QMultiLineEdit_hook_hook_returnPressed(QMultiLineEdit_hookH(Hooks), Method);
end;

Click here to go back to the top of this page


Can I Use Message Handler Methods in Kylix To Pick Up Qt Events?

This is a common issue. People who are used to working with Delphi are used to reacting to Windows messages using one of two approaches:

  1. Overriding the virtual TControl.WndProc method.
    This method is passed every Windows message as a record in a var parameter. A simple (but potentially lengthy) case statement allows you to examine the message ID and respond to messages of interest. You can see the WndProc method from TWinControl here:
    type
      TWinControl = class(TControl)
      ...
      protected
        procedure WndProc(var Message: TMessage); override;
      ...
      end;
    ...
    procedure TWinControl.WndProc(var Message: TMessage);
    var
      Form: TCustomForm;
    begin
      case Message.Msg of
        WM_SETFOCUS:
          begin
            Form := GetParentForm(Self);
            if (Form <> nil) and not Form.SetFocusedControl(Self) then Exit;
          end;
        WM_KILLFOCUS:
          if csFocusing in ControlState then Exit;
        WM_NCHITTEST:
          begin
            inherited WndProc(Message);
            if (Message.Result = HTTRANSPARENT) and (ControlAtPos(ScreenToClient(
              SmallPointToPoint(TWMNCHitTest(Message).Pos)), False) <> nil) then
              Message.Result := HTCLIENT;
            Exit;
          end;
        WM_MOUSEFIRST..WM_MOUSELAST:
          if IsControlMouseMsg(TWMMouse(Message)) then
          begin
            { Check HandleAllocated because IsControlMouseMsg might have freed the
              window if user code executed something like Parent := nil. }
            if (Message.Result = 0) and HandleAllocated then
              DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam);
            Exit;
          end;
        WM_KEYFIRST..WM_KEYLAST:
          if Dragging then Exit;
        WM_CANCELMODE:
          if (GetCapture = Handle) and (CaptureControl <> nil) and
            (CaptureControl.Parent = Self) then
            CaptureControl.Perform(WM_CANCELMODE, 0, 0);
      end;
      inherited WndProc(Message);
    end;
  2. Using message handling methods.
    To avoid the lengthy case statement in a Delphi WndProc, you can also use message handling methods, which allow specific methods to be automatically executed in response to the appropriate messages. Here are some of the TWinControl message handlers.
    type
      TWinControl = class(TControl)
      private
      ...
        procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
        procedure WMCharToItem(var Message: TWMCharToItem); message WM_CHARTOITEM;
        procedure WMParentNotify(var Message: TWMParentNotify); message WM_PARENTNOTIFY;
      ...
      end;

In both cases, the message information is passed in the form of a record. WndProc takes a TMessage var parameter, but the message handlers have special message-specific records, such as TWMSysCommand. These records all represent the same amount of data, but there are many redefinitions of TMessage defined in the Messages unit that make it easier to access the information passed with each message.

In a CLX application, you can still use message handlers for messages that are sent internally around the application with TObject.Dispatch or TWidgetControl.Broadcast; the CLX version of the TMessage record type is defined in QControls and only has one Cardinal field called Msg.

However the quesion is not asking whether Windows messages can be emulated in Kylix. It is asking whether Qt widget events can be redirected to message handlers in an automated fashion, in just the same way as Windows messages are in VCL applications.

The answer is a positive one, so long as we understand how message handler methods are located. The key point to understand is that the message handling methods expect to be passed a block of data (typically a record, but not necessarily - it could be an array, for example), where the first four bytes contain the message identifier. All the Windows message records have the message identifier as the first (Cardinal) field.

To simplify the processing of Qt events for any given control (including a form), you override the EventFilter method (the effective equivalent of the VCL WndProc method) and add some code to set up a simple record. The record would have an ordinal field to identify the event type and any other information that you care to pass (typically this would be the event object handle).

Much like the VCL message-handling support, you can elect to define a variety of same-sized records, one for each event type of interest, to make the message handling method have an easier job with the event object. For example, the generic record could define a QEventH handle as the second field. A record dedicated to mouse events could define a QMouseEventH handle instead, and so on.

Here is some sample code that shows how this works. It shows how to redirect Qt events directed at the form into message handling methods. It also shows a generic TQtEvent record (akin to TMessage) and a specific TQtMouseEvent record (akin to the VCL TWMMouse record).

uses
  Qt, ...
...
type
  TQtEvent = record
    EventType: QEventType;
    Event: QEventH;
  end;

  TQtMouseEvent = record
    EventType: QEventType;
    Event: QMouseEventH;
  end;

  TForm1 = class(TForm)
    ListBox1: TListBox;
  private
    procedure Msg(const Msg: String; const Args: array of const);
  protected
    function EventFilter(Sender: QObjectH; Event: QEventH): Boolean; override;
  public
    procedure QMouseMove(var Event: TQtMouseEvent); message Ord(QEventType_MouseMove);
    procedure QMousePress(var Event: TQtMouseEvent); message Ord(QEventType_MouseButtonPress);
    procedure QMouseRelease(var Event: TQtMouseEvent); message Ord(QEventType_MouseButtonRelease);
  end;
...
function TForm1.EventFilter(Sender: QObjectH; Event: QEventH): Boolean;
var
  QtEvent: TQtEvent;
begin
  Result := inherited EventFilter(Sender, Event);
  QtEvent.EventType := QEvent_type(Event);
  QtEvent.Event := Event;
  Dispatch(QtEvent);
end;

procedure TForm1.Msg(const Msg: String; const Args: array of const);
begin
  ListBox1.Items.Add(Format(Msg, Args));
  ListBox1.ItemIndex := ListBox1.Items.Count - 1
end;

procedure TForm1.QMouseMove(var Event: TQtMouseEvent);
begin
  Msg('Mouse is at (%d,%d)',
    [QMouseEvent_x(Event.Event), QMouseEvent_y(Event.Event)]);
end;

procedure TForm1.QMousePress(var Event: TQtMouseEvent);
begin
  Msg('Mouse pressed at (%d,%d)',
    [QMouseEvent_x(Event.Event), QMouseEvent_y(Event.Event)]);
end;

procedure TForm1.QMouseRelease(var Event: TQtMouseEvent);
begin
  Msg('Mouse released at (%d,%d)',
    [QMouseEvent_x(Event.Event), QMouseEvent_y(Event.Event)]);
end;

Of course I realise that each class of control that you want to use message handling methods (which I should now be referring to as Qt event handling methods) will need the EventFilter overridden. I'm currently looking into a way of automating this in some way.

Click here to go back to the top of this page


Can I Write A Window Manager For X Windows In Kylix?

You certainly can and a keen Kylix developer has already written one, called XPde (formerly known as XPwm, and before that known as w2kwm), which emulates the Windows XP user interface. Visit http://www.xpde.com to download the binaries and source. Browse through the source to see how it was written.

Click here to go back to the top of this page


Kylix Easter Eggs

Kylix, like most commercial software products, has a few Easter Eggs tucked away inside it. You can see the full details for Kylix 1 by clicking here.

Click here to go back to the top of this page


Runtime Error 230 Starting Kylix

The most likely cause for this problem is that your JPEG library has been compiled incorrectly. Whilst the library is apparently the correct version (since Kylix installed correctly), one of the values defined in the library is causing problems with the JPEG image used in the Kylix splash screen.

To find out if this is true, try passing the -ns parameter to the startkylix script which disables the splash screen and see the IDE starts up okay. Assuming it does, you can perform a further test by choosing Help | About to see the About box. This dialog also has a JPEG image and, if the JPEG library is indeed compiled incorrectly, you could well get:

JPEG Error #21

To fix the problem, one option is as follows. You will need to obtain the JPEG library source, possibly from your Linux installation CDs, or from http://www.ijg.org. Look in the file jpeglib.h and locate the symbol D_MAX_BLOCKS_IN_MCU (the JPEG decompressor's limit on blocks per MCU). If it is being defined as anything other than 10, change it back to 10 (it is apparently often changed to higher values, such as 64).

Now you can follow the directions that come with the library source code to recompile and install it. This should remedy the problem.

An alternative solution would be to install the correctly compiled library from the Kylix CD. You can find the RPM file in the CD's patches/jpeg6.2.0 directory. Your current library can be updated by navigating to the CD directory and running:

rpm -Uhv libjpeg-6.2.0-62.i386.rpm

Click here to go back to the top of this page


Error -10 From The Installation Program

Because of an issue with the RPM installer, where it incorrectly parses the destination directory, attempts to install Kylix on Red Hat Linux 7.1 (and probably other, more recent distros) are met with the error:

Error Code: -10. Setup cannot continue. If this error persists, please contact Technical Support.

Fortunately, Kylix can be installed without using RPM by running the setup.sh script with the -m parameter (which tells the script not to use RPM). Kylix should then install fine.

Click here to go back to the top of this page


The font matrix generation dialog hangs when I run Kylix for the first time

This may happen when running a 2.4 level kernel. Currently the only workaround is to prevent the dialog from appearing:

mv /bin/transdlg /bin/transdlg.hide.me

Click here to go back to the top of this page