Tuesday, February 24, 2009

exit(0)




Say that your application has subclassed QApplication and that you have some evaluation logic in the constructor that needs to exit the application if some condition is not met like so:


int main(int argc, char **argv) {
MyQApplication app(argc, argv);
return app.exec();
}

MyQApplication::MyQApplication(int argc, char** argv) :
QApplication(argc, argv) {

// want to be able to exit here
}


So what's the issue? Well QCoreApplication::exit() and QCoreApplication::quit()
(which is the same as exit(0)) needs the event loop to be up and running. As the documentation for QCoreApplication::exit() says says: "If the event loop is not running, this function does nothing."

Now from a design perspective exiting from the constructor like this is probably not the best way to go about things. But, for the sake of argument say that you really have to. And have to without refactoring the code.

So far the only two options I've come across that let's you keep your old design is qFatal() and std::exit(). However, they both leave some to be desired. qFatal() will print and "Aborted" at the end and that is no good if you have an internationalized application. std::exit(0) will require reaching outside of Qt, which is fine, but you need to update the compiler include path and that could open up issues when compiling on different (non-standard) systems.

Now if you're not to lazy a re-design is in place. I posed the question to the qt-interest mailing list and got a couple of good responses.

Arnold Krille suggested to create a bool MyQApplication::shouldRun() and then check before calling app.exec() like so:


int main(int argc, char **argv) {
MyQApplication app(argc, argv);

if (app.shouldRun())
return app.exec();
}


Scott Aron Bloom pointed out that throwing an exception is considered better practice then calling exit() directly.


MyQApplication::MyQApplication(int argc, char** argv) :
QApplication(argc, argv) {

if(errorConditionExisted)
throw ...
}

Monday, February 23, 2009

Damn bugs!



Well, bug at least. Had to push a bugfix release out. The super cool -flags option turned out to be broken. QA has been notified and have already updated their testing routines.

Get the source, ebuild, deb and rpm from the usual location.

Flags



If you're running in non KDE environment, or just missing the l10n part of KDE localizations module here is an archive containing the flags.

Use the -flags argument to specify the location of the extracted folder if it's not under /usr/share/locale/.

Sunday, February 22, 2009

New release



OK, so the idea was that starting from version 0.60 we would have full KDE support. That did not happen, but I'm happy to announce one of the strongest versions ever. Still pure Qt, still live! spell checking.

A lot of effort went into this one and there where plenty of new improvements and issues fixed. Some of the major updates are:


  • Updated the way words get entered, should be a lot more flexible now
  • Added XKb support to get the keyboard layout on non KDE systems
  • Added Ukrainian translations, thanks to Yuri Chornoivan
  • Fixed "replace in client", should be stable now
  • Fixed issue 1 and issue 5, where some correctly spelled words being treated as misspelled
  • Changed -language command line parameter to set application language


Please see the full Changelog for more information.

There is now a Kubuntu deb and a Fedora RPM as well as the regular Gentoo ebuild.

Enjoy!

Thursday, February 19, 2009

Got atoms?



This is a piece of useful code I forgot to save a while back so I had to take some time to research it again. It's for getting Xlib window property information. Change the XA_STRING to fit your needs based on the types in X11/Xatom.h.


Atom* atoms;
int num_prop_return = 0;
atoms = XListProperties(m_display, window, &num_prop_return);

for(int i = 0; i < num_prop_return; i++) {
Atom atom = *atoms;
QString name = XGetAtomName(m_display, atom);
atoms++;

Atom type;
int format;
long offset = 0;
long length = 8192;
unsigned long nitems, after;
unsigned char *data = 0;
XGetWindowProperty(m_display,
window,
atom,
offset,
length,
False,
XA_STRING,
&type,
&format,
&nitems,
&after,
&data);
qDebug() << name << QString::fromLatin1((const char*) data);
}

XFree(atoms);



References:
Xlib Properties and Atoms
qapplication_x11.cpp

Wednesday, February 18, 2009

XKb support



Finally done! Took half a day, but time well spent as I've wanted to implement this for months and never figured out a good way. Parsing these long strings is not what I hoped for, but at lease now I have something to build on. Remember what trigged this in the first place is the broken QApplication::keyboardInputLocale().

Issues



I'm happy to report that a bunch of issues have been resolved these last few days.

A couple worth to mentioning are issue 1 and issue 5 which are both related to faulty string conversions causing Aspell to think that a word is misspelled when in fact it's not. Be careful if you need a const char * and you're using the QString "to" functions such as QString::toUtf8() and QString::toLocal8Bit(). Apparently the latter is to be preferred.

More issues are being resolved and the word checking and displaying algorithm has been rewritten to be a bit more flexible. There is also a new keyboard layout and language override in place. The next release is just around the corner so stay tuned.

Monday, February 16, 2009

Got keyboard layout?



You've probably heard me ranting about this before, especially if you know me in person, but Xlib development and XKb in particular is not the most pleasant think in the world. And this could not be more true when you're coming from a pampered and spoiled Qt environment.

Try getting the current keyboard layout from an X11 session and you'll quickly understand what I'm talking about. Why not just use QApplication::keyboardInputLocale()? Well, true, this is what you would usually do:


QString keyboardLayout = qApp->keyboardInputLocale().name();


Which should give you en_US if you're running under that locale. However QApplication::keyboardInputLocale() is broken. So now you're on your own.

The issue here is that there is no simple way of doing that in Xlib/Xkb. The X server loads the layouts on startup and compiles them from the symbol files usually defined in /usr/share/X11/xkb/symbols into key syms and key codes, which we'll call key maps. Now I might be wrong about this, but I did spend more then just a few hours trying to figure this out. Surprisingly, the X devs did not think it necessary to know the symbol layout name at runtime. You can get the compiled keymaps and group names, which for the first in the us symbols file is name[Group1]= "USA";. This is how you would go that:


#include <X11/XKBlib.h>
...

XkbDescPtr keyboard = XkbGetKeyboard(display,
XkbAllComponentsMask,
XkbUseCoreKbd);

XkbStateRec state;
XkbGetState(m_display, XkbUseCoreKbd, &state);

unsigned int group = (unsigned int) state.group;

QString symbols = XGetAtomName(display, keyboard->names->symbols);
QString name = XGetAtomName(display, keyboard->names->groups[group]);

qDebug() << "symbols" << symbols;
qDebug() << "name" << name;

...


However, I have little need for a "USA". I want a "us". So what did I discover in my more then a few o hour long research. Basically two ways of doing this. The VirtualBox guys have a list of their own keyboard symbols (key maps) and compare them to the current key maps from the X server. The one that closes resembles the one form the X server will be the best guess. Crazy...

The other approach I found was to parse the symbols list together with the current group index. While this is slightly more intuitive then the VirtualBox approach, it's still insane that one has to go out of their way like this.

This is what XGetAtomName(display, keyboard->names->symbols) call above returns


pc_us_se_2_il_3_nec_vndr/jp_4_inet(evdev)_group(alt_shift_toggle)


Which is similar to what you can get from the command line using .


xkb_symbols { include "pc+us+se:2+inet(evdev)+group(alt_shift_toggle)" };


Now all that's left is to parse that string to get and combine it with the group index to get the current one. I'll let you do the math from here.

Edit: Be careful where you call those XKb functions from as you might get synchronization issues such as Xlib: unexpected async reply

Edit: Looks like QApplication::keyboardInputLocale() works if there is only one keyboard layout.

Edit: Got a task opened by Qt.

Debug output is the key



Debuggers are great, but having to step through code is not always ideal. Good strategical debug output can save you a lot of time when trying to track down an issue. Especial if the debugging is being done by a non-developer.

As you probably know, adding a debug trace in Qt is child's play. Just do a


#include <QtDebug>


and then put a


qDebug() << "This is a debug message";


wherever you need it.

To compile Kisa with debug symbols just execute these commands in the extracted source folder;

qmake
make debug


To start the debug enabled version just call bin/kisa, no need to install it. Some of the paths might not be correct, especially for the country_language_mappings.txt, but now you have debug output to track down the issue.

That's it!

Sunday, February 15, 2009

Creating custom deb packages is a pain



As a long time Gentoo user and maintainer I sometimes find myself crying at the time it takes to get a package compiled. Especially if you're updating or installing something that is needed super pronto. Now this is nothing unique to Gentoo, it's a problem for any source-based distro. However, today I got reminded (again) why a source-based solution gives users and developers superior control.

Have you ever tried crating an i686 package for a Debain based system? If not then let me spare you the agony and tell you to not waste your time. Now this is not a shortcoming in the package creating system, which by the way is a nightmare of configuration files and scripts, but rather a deficiency in the package distribution and management system that doesn't allow i686 pages to be installed. Yes, that right, you can run a i686 host and you can compile and run i686 applications, but you can't install them. Well, not at least pure i686 packages. This is being discussed now so hopefully things will improve.

If by any chance you would like to try this is what I did:

First make sure the necessary build tools are available by installing these packages:


apt-get install build-essential pbuilder debhelper dh-make


Then follow the packaging guide and make sure that you at least have your control, copyright and rules files in order. This horribly styled page breaks it down pretty well.

Add these lines to the top of the rules file to override the default compiler and compiler flags. Change according to your needs


CC = gcc
CFLAGS = -march=i686 -02 -pip


Also update the build make target in that file as well, so it looks something like this:


build-stamp: configure-stamp
...
$(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)"


To force a special architecture build, in this case i686, use:


dpkg-buildpackage -ai686 -b -uc -rfakeroot


Remember, you probably won't be able to install the resulting .deb unless you tweak the Architecture tag in the control file.

Good luck!

Edit: The above should work to create i686 optimized i386 packages. If that makes any sense...

Saturday, February 14, 2009

Valentine's day release



Consistency? No, not much around here. But I do have a great application that solves a fundamental problem — spell checking. And what gift could be better to give to your loved one on this day.

Finally a new release out. Most changes for version 0.56 are under the hood, making the the code structure cleaner and more manageable. Other noteworthy changes are KDE 4 support turned on as default, better icon integration, improved pop-up handling and an updated default install path ("/usr/local").

All good stuff, but there are two hacks that I would especially like to bring up. The first is Kxkb integration over D-Bus as getKeyboardInputLayout and the accompanying event notifications are broken in Qt. What this means is that Kisa will now poll the keyboard layout whenever a new word is entered instead of waiting for the event notification. Obviously one needs to have Kxkb running. It suprisingly easy to do D-Bus calls, just do a:


QDBusInterface kxkb("org.kde.kxkb", "/kxkb", "org.kde.KXKB");
QDBusReply reply = kxkb.call("getCurrentLayout");


The other hack makes use of the INSTALL_ROOT variable. Even though it is being deprecated in favor of DISTDIR by the GNU Make crowd, it still shows up everywhere in the qmake generated makfiles. Problem is that Qt forgot to give a easy way to set it in the .pro file. Currently, it has to be passed on the command line when invoking make or set as an environment variable. Well, not anymore if you're using Kisa. Here is one way to do it in the toplevel project file:


isEmpty(INSTALL_ROOT) {
INSTALL_ROOT = /usr/local
}

INSTALL_ROOT = $$replace(INSTALL_ROOT, "/", "\/")

QMAKE = $$[QMAKE]
isEmpty(QMAKE) {
QMAKE = qmake
}

system("cd src/; $$QMAKE src.pro")
system("cd src/lib/; $$QMAKE lib.pro")
CMD = "find -iname '$$MAKEFILE*' | xargs sed -i 's/INSTALL_PROGRAM.*=.*/&\nINSTALL_ROOT = $$INSTALL_ROOT/'"
system($$CMD)


Note though that there are several problems with this approach. It will only work on a project that has a top level .pro file as we are generating makefiles on the lower level project files. It will also not work if passing -recursive to qmake. However, the advantage of having an easy way to set INSTALL_ROOT outweighs these shortcomings by a long shot. Full changelog is here.

Enjoy!


Edit: Got a task opened by Qt.