The cross platform code that isn't.
A couple of weeks ago, another piece of wxWidgets infrastructure in the Alter Aeon client was replaced with custom code. This time, it was the bulk of the keyboard handling code that fell in battle.
In theory, the wxWidgets keyPressEvent is where you should trap out various key combinations; things like 'a', the escape key, and 'control-c' pairs. Certainly under linux, nearly all of these worked correctly, and the ones that didn't were obscure and not really worth hunting down. When I first ported to Win32, all that changed.
In Win32 builds, a lot of keys were not handled by the key press event. You'd press the key combination, and nothing would happen. It was like the operating system was trapping out the key combination and never bothering to pass it up the stack. As it turns out, that's pretty much exactly what was happening.
I did eventually get the Win32 builds to work by intercepting a handful of keys at the keyDownEvent and keyUpEvent layers. When I finally found these, it was hugely helpful; in the end, I trapped on the order of 30 key combinations to do away with things like annoying system beeps and idiotic behavior of the default keystrokes, in addition to handling our own special control combinations.
Enter Mac OSX. Enter a whole new raft of keyboard weirdness. I hacked on this for about an hour, and quickly realized that I was on the path to madness. The code was becoming a rat's nest of ifdefs with key combinations trapped out in multiple different layers in different operating systems.
About the only thing that could be called consistent between any of the operating systems was that the keyDownEvent and keyUpEvent were always reliable. The solution then seemed obvious: move everything down into the keyDownEvent, and ignore everything else. Intercept the keyboard control -before- wxWidgets could do anything stupid with it.
Keyboard control now works consistently and reliably on all three platforms.
Showing posts with label wxwidgets sucks. Show all posts
Showing posts with label wxwidgets sucks. Show all posts
Monday, September 26, 2011
Saturday, July 9, 2011
wxWidgets GTK font rendering
In my previous post, I managed to fix the bugs in wxDC::DrawPoint() by using my own custom drawing class. Unfortunately, a lot of the places I where I used DrawPoint were also places where I needed to draw text, and in order to draw text I needed a wxDC. No problem, I'll just draw the text using a temporary wxMemoryDC on the bitmap, and to make sure there's no clobbering problems I'll make sure that I don't intermingle direct drawing scope with the DC object scope.
This appeared to work, except for the scrolling/updating windows. For some reason, even though there was a Clear() call before the font rendering, the windows would never clear. If I destroyed and recreated the bitmap every time, it worked fine but was very slow. If I cleared after drawing the text, the clear worked. If I cleared before drawing the text, the clear did not work.
Eventually I figured out the reason:
Under wxGTK, wxDC drawing and rendering are done to a separate bitmap/bitmap layer that is permanently attached to the real bitmap. When font data is drawn using wxDC::DrawText(), the text is drawn to the hidden layer, then the hidden layer is blitted in its entirety on top of the real bitmap. The end result is that anything you do prior to constructing the DC and rendering text with that DC is overwritten/deleted when the DC destructs.
Just how the hell does this make any sense? Nevermind, forget I asked. On the plus side, this explains why full screen font rendering is so slow in wxGTK. I never could figure that part of it out.
For roughly a day, I was utterly at a loss when faced with this problem. Switch back to the old code and lose the MacOS X platform; stay with the new code and lose wxGTK (and possibly other platforms, I only ever tested it on GTK.) I couldn't even think of a way to work around it.
Eventually, a possible solution presented itself: add font rendering to my custom bitmap class. I wasn't real clear on how to do this at first, but the key breakthrough came when I realized I could just use a cheap glyph rendering scheme. Use wxDC to draw each of the letters I needed (ascii chars 32 through 126) into tiny, clean bitmaps, then use a modified alpha channel copy routine to draw them to my custom bitmaps.
I'm proud to say that I now have proper text rendering and graphical drawing primitives on all three platforms. And to think that all I needed to do was rewrite the entire wxDC rendering system to do it!
I should have just done it in the first place, as I recommended to myself in a past life. Sometimes our older selves are no wiser than we are in youth.
This appeared to work, except for the scrolling/updating windows. For some reason, even though there was a Clear() call before the font rendering, the windows would never clear. If I destroyed and recreated the bitmap every time, it worked fine but was very slow. If I cleared after drawing the text, the clear worked. If I cleared before drawing the text, the clear did not work.
Eventually I figured out the reason:
Under wxGTK, wxDC drawing and rendering are done to a separate bitmap/bitmap layer that is permanently attached to the real bitmap. When font data is drawn using wxDC::DrawText(), the text is drawn to the hidden layer, then the hidden layer is blitted in its entirety on top of the real bitmap. The end result is that anything you do prior to constructing the DC and rendering text with that DC is overwritten/deleted when the DC destructs.
Just how the hell does this make any sense? Nevermind, forget I asked. On the plus side, this explains why full screen font rendering is so slow in wxGTK. I never could figure that part of it out.
For roughly a day, I was utterly at a loss when faced with this problem. Switch back to the old code and lose the MacOS X platform; stay with the new code and lose wxGTK (and possibly other platforms, I only ever tested it on GTK.) I couldn't even think of a way to work around it.
Eventually, a possible solution presented itself: add font rendering to my custom bitmap class. I wasn't real clear on how to do this at first, but the key breakthrough came when I realized I could just use a cheap glyph rendering scheme. Use wxDC to draw each of the letters I needed (ascii chars 32 through 126) into tiny, clean bitmaps, then use a modified alpha channel copy routine to draw them to my custom bitmaps.
I'm proud to say that I now have proper text rendering and graphical drawing primitives on all three platforms. And to think that all I needed to do was rewrite the entire wxDC rendering system to do it!
I should have just done it in the first place, as I recommended to myself in a past life. Sometimes our older selves are no wiser than we are in youth.
Labels:
client,
software engineering,
toolkits,
wxwidgets sucks
wxDC DrawPoint broken under wxMAC
To follow up on my previous post, I had written my own routines for handling alpha channel support under wxWidgets, and finally everything seemed to be working properly. Both Win32 and Linux/GTK builds seemed to work exactly as expected using my alpha channel bitmaps where I needed them. For most other places, I still used wxDC primitives, as wxDC direct drawing seemed to be a bit faster than going through a bitmap/blit interface, and I still needed to use wxDC to render fonts.
I figured porting to MacOS X would be a snap, and for the most part it wasn't too bad. There were some issues with Repaint() not working the same, but adding a few explicit paint calls in the right place fixed that up.
However, something was wrong with some of the graphics in the various windows. The sky bar, normally with lots of little stars in it, still had stars in it, but instead of one pixel there were two: one at offset (0, 0), and one at offset (1, 1). I also noticed 'fuzz' on the corners of various windows and buttons, and in a handful of other locations.
Long story short, wxDC::DrawPoint() draws two pixels under wxMAC. Not one, but two. I found a couple bug reports regarding this from a few years ago, where it was basically acknowledged as a "verified bug", and the ticket was never closed. Advice was "these routines are slow and shouldn't be used for high performance drawing". Thanks guys.
I have some mighty fine choice words to say to the developers regarding this, but I'll try to keep this post on topic.
If something as fundamental as DrawPoint() is malfunctioning on a major development platform, I figured it would probably be a good idea to transition away from the classes that implement it in favor of my own drawing routines. So, I started using my bitmap class, which just happens to have a DrawPixel() function that works properly.
As expected, this fixed the drawing problems on all three platforms. But alas, using my bitmap class exposed another set of very nasty wx bugs, which I'll discuss in the next post.
I figured porting to MacOS X would be a snap, and for the most part it wasn't too bad. There were some issues with Repaint() not working the same, but adding a few explicit paint calls in the right place fixed that up.
However, something was wrong with some of the graphics in the various windows. The sky bar, normally with lots of little stars in it, still had stars in it, but instead of one pixel there were two: one at offset (0, 0), and one at offset (1, 1). I also noticed 'fuzz' on the corners of various windows and buttons, and in a handful of other locations.
Long story short, wxDC::DrawPoint() draws two pixels under wxMAC. Not one, but two. I found a couple bug reports regarding this from a few years ago, where it was basically acknowledged as a "verified bug", and the ticket was never closed. Advice was "these routines are slow and shouldn't be used for high performance drawing". Thanks guys.
I have some mighty fine choice words to say to the developers regarding this, but I'll try to keep this post on topic.
If something as fundamental as DrawPoint() is malfunctioning on a major development platform, I figured it would probably be a good idea to transition away from the classes that implement it in favor of my own drawing routines. So, I started using my bitmap class, which just happens to have a DrawPixel() function that works properly.
As expected, this fixed the drawing problems on all three platforms. But alas, using my bitmap class exposed another set of very nasty wx bugs, which I'll discuss in the next post.
Labels:
bugs,
software engineering,
toolkits,
wxwidgets sucks
wxwidgets has no alpha channel
A few months after I ported the Alter Aeon client to the wxWidgets toolkit, I decided I wanted to add alpha overlay support to the automap and main window.
Encouragingly enough, this appeared to be trivially supported: wxColor included an alpha value, bitmaps could be defined as having an alpha channel, etc. Pretty much everything looked good to go, so I started experimenting with it.
Alas, it was not to be. It turns out that there are precisely three places where the alpha channel is used:
1) loading a PNG from PNG data that has an alpha channel,
2) drawing a bitmap containing alpha channel data using wxDC::DrawBitmap, and
3) wxDC::DrawText, which politely draws text on top of the underlying pixels.
For all other locations, there simply should have been this assert:
wxASSERT("Alpha channel is stubbed here and works on precisely zero platforms ever, ha ha, the joke is on you")
After upgrading to wx 2.9 and a pile of scouting around, I came across wx/rawbmp.h, which has some direct access functions that allow access to the alpha channel. With this, I managed to get alpha channel working - but there were other problems with 2.9, so I downgraded back to stable, 2.8.12.
The alpha channel code that worked under 2.9 works properly under wx 2.8.12 in wxMAC and wxGTK, but apparently isn't implemented under Win32, which happens to be my main development platform.
Rather than bash my skull repeatedly against the wall, I did what any sane developer would do: throw out the broken toolkit module, and write one that works. In the end, I ended up with a wxBitmap derived class that has drawing functions that actually work properly and support alpha channel drawing when I need them to.
However, there's a problem with this as well, which I'll detail in a later post.
Encouragingly enough, this appeared to be trivially supported: wxColor included an alpha value, bitmaps could be defined as having an alpha channel, etc. Pretty much everything looked good to go, so I started experimenting with it.
Alas, it was not to be. It turns out that there are precisely three places where the alpha channel is used:
1) loading a PNG from PNG data that has an alpha channel,
2) drawing a bitmap containing alpha channel data using wxDC::DrawBitmap, and
3) wxDC::DrawText, which politely draws text on top of the underlying pixels.
For all other locations, there simply should have been this assert:
wxASSERT("Alpha channel is stubbed here and works on precisely zero platforms ever, ha ha, the joke is on you")
After upgrading to wx 2.9 and a pile of scouting around, I came across wx/rawbmp.h, which has some direct access functions that allow access to the alpha channel. With this, I managed to get alpha channel working - but there were other problems with 2.9, so I downgraded back to stable, 2.8.12.
The alpha channel code that worked under 2.9 works properly under wx 2.8.12 in wxMAC and wxGTK, but apparently isn't implemented under Win32, which happens to be my main development platform.
Rather than bash my skull repeatedly against the wall, I did what any sane developer would do: throw out the broken toolkit module, and write one that works. In the end, I ended up with a wxBitmap derived class that has drawing functions that actually work properly and support alpha channel drawing when I need them to.
However, there's a problem with this as well, which I'll detail in a later post.
Labels:
client,
software engineering,
toolkits,
wxwidgets sucks
Wednesday, October 7, 2009
Yet Another wxWidgets Fail
I've been trying off and on for a while to get transparency to work in my Linux/GTK builds, and for whatever reason it's just not working (at all.) After running some experiments last night, the best I can determine is that there simply is no alpha channel available in the wxBitmap/wxMemoryDC that I'm trying to use.
It's not that I didn't try to get one. Supposedly all you need to do is construct bitmaps with a 32 bpp and everything else will take care of itself; however, even simple operations like putting a pixel or drawing onto the DC returns 0xFF for the alpha channel every time. The alpha simply disappears. Because the alpha is gone, when I attempt to draw or blit the bitmaps, I get a full overwrite instead of a proper alpha blend.
Once again, it's beyond me how something so simple can silently fail so catastrophically while giving no indication of error or dysfunction. I'm using the modules according to the published API, and the expected results do not occur.
Naturally, there is coupled to this another issue: wxWidgets does not have very good drawing/shading and blending primitives. I could probably draw what I want using the tools, but it will be costly, both in time and processing power.
The obvious two solutions present themselves:
1) Find another library, such as OpenGL, that can I can integrate into the toolkit, or
2) Write my own graphics subsystem.
Size is, in my mind, at a premium here, so I would prefer not to bloat out the executable with another five megabytes of drawing and 3D utilities. It also just so happens that I've done a lot of game and graphics programming in the past. Graphics programming that included various primitive libraries.
Now, the only issue is how to get direct access to wxBitmap data so that I can do my drawing elsewhere, then copy into the bitmap for blitting. It just so happens that there's an undocumented 'wxRawBitmap' class that looks like it might do the job. Hooray for undocumented features. What the hell.
It's not that I didn't try to get one. Supposedly all you need to do is construct bitmaps with a 32 bpp and everything else will take care of itself; however, even simple operations like putting a pixel or drawing onto the DC returns 0xFF for the alpha channel every time. The alpha simply disappears. Because the alpha is gone, when I attempt to draw or blit the bitmaps, I get a full overwrite instead of a proper alpha blend.
Once again, it's beyond me how something so simple can silently fail so catastrophically while giving no indication of error or dysfunction. I'm using the modules according to the published API, and the expected results do not occur.
Naturally, there is coupled to this another issue: wxWidgets does not have very good drawing/shading and blending primitives. I could probably draw what I want using the tools, but it will be costly, both in time and processing power.
The obvious two solutions present themselves:
1) Find another library, such as OpenGL, that can I can integrate into the toolkit, or
2) Write my own graphics subsystem.
Size is, in my mind, at a premium here, so I would prefer not to bloat out the executable with another five megabytes of drawing and 3D utilities. It also just so happens that I've done a lot of game and graphics programming in the past. Graphics programming that included various primitive libraries.
Now, the only issue is how to get direct access to wxBitmap data so that I can do my drawing elsewhere, then copy into the bitmap for blitting. It just so happens that there's an undocumented 'wxRawBitmap' class that looks like it might do the job. Hooray for undocumented features. What the hell.
Labels:
client,
wxwidgets sucks
Saturday, August 29, 2009
Client update
I've been working on the DClient code for the last couple of days, and it's coming along nicely. I had intended to do another interim release on thursday; after discovering that the Windows build of fundamental display primitives is vastly different than the Linux version, I had to go back to the drawing board and come up with a rendering/display system to avoid those issues.
Conveniently, with the new rendering system, the original problem that forced me to write the rendering system has vanished. I could probably figure it out if necessary, but at this point, I just want to get my work done. I'm still keeping the rendering system though; every piece of WX I replace is one step farther away from needing to use it at all. At some point, all that will be left is the event loop, wxDC functions, and Win32 resource management.
The improvements in this version of the client are primarily a simpler user interface with a lot less clutter. We also have popup-style windows for things like score, equipment, inventory and the who list. I've also prettied up some of the buttons, and have enough infrastructure in place to possibly start doing some window decorations.
If everything goes stunningly well, I should be able to get a testing release out Sunday night. More likely however, it'll be monday or later.
Conveniently, with the new rendering system, the original problem that forced me to write the rendering system has vanished. I could probably figure it out if necessary, but at this point, I just want to get my work done. I'm still keeping the rendering system though; every piece of WX I replace is one step farther away from needing to use it at all. At some point, all that will be left is the event loop, wxDC functions, and Win32 resource management.
The improvements in this version of the client are primarily a simpler user interface with a lot less clutter. We also have popup-style windows for things like score, equipment, inventory and the who list. I've also prettied up some of the buttons, and have enough infrastructure in place to possibly start doing some window decorations.
If everything goes stunningly well, I should be able to get a testing release out Sunday night. More likely however, it'll be monday or later.
Labels:
client,
toolkits,
wxwidgets sucks
Wednesday, August 12, 2009
wxWidgets sucks
This is becoming less of a mud development blog than a 'bitch about wxWidgets sucking' blog. I've run into yet another piece of wxWidgets wierdness: wxBitmapButtons don't work. The documentation claims bitmaps can be used instead of the default graphics; trying this results in a very small bitmap wedged inside a ten pixel thick border of default graphics. There appears to be no way to turn this behaviour off.
I'm sure that there's a perfectly good reason why it behaves this way. It might be intentional; it might be that my development platform (GNU/Linux) is more buggy than other platforms; it might be that I'm simply terrible at operating this toolkit. Regardless, its unacceptable, and it is only with great reluctance weighing my application requirements that I did not switch to something else this very night.
Screw this toolkit. I wrote a text window class for exactly this reason, and quite frankly if I'd have done it to start with I'd have saved myself a lot of time. I will not make that mistake again, and as of right now I'm creating an image button class. I might not know what I'm doing, but at least I'll know it works properly, on all the platforms I need it to.
I get the feeling that by the time I'm done with this, I'll have using only the event handling loop and the font selection dialog. It seems like those are the only widgets that actually function as documented.
I'm sure that there's a perfectly good reason why it behaves this way. It might be intentional; it might be that my development platform (GNU/Linux) is more buggy than other platforms; it might be that I'm simply terrible at operating this toolkit. Regardless, its unacceptable, and it is only with great reluctance weighing my application requirements that I did not switch to something else this very night.
Screw this toolkit. I wrote a text window class for exactly this reason, and quite frankly if I'd have done it to start with I'd have saved myself a lot of time. I will not make that mistake again, and as of right now I'm creating an image button class. I might not know what I'm doing, but at least I'll know it works properly, on all the platforms I need it to.
I get the feeling that by the time I'm done with this, I'll have using only the event handling loop and the font selection dialog. It seems like those are the only widgets that actually function as documented.
Labels:
toolkits,
wxwidgets sucks
Subscribe to:
Posts (Atom)