API Programming: What’s behind the PB GUI objects

For Windows, this question is quickly answered: HWND. Pretty much everything is of this basic “window”-type and most API commands that deal with windows can be used on most GUI objects. You can also find many code examples on the forums that deal with a lot of common API tasks. The situation is different for Linux and OSX. Here, different Gadgets may have different underlying objects and so it is not entirely clear what functions can be used. There are also a lot less examples around for these systems. Of course when developing programs that run on multiple OS, you will need solutions for all of them.

To help this situation a bit, i have compiled a list of what are the underlying objects behind the PB gadgets and other GUI objects. You can get access to these using functions such as GadgetID(), WindowID(), etc.

Disclaimer:

This information is internal stuff. While many of these things haven’t changed in many years, it is still possible that we decide to change something in the future. Also some Gadgets are heavily modified for PB with custom subclasses or callbacks which also may change in the future and break your code. So as usual: Feel free to use this information, but if it breaks, its not our problem.

So here it goes:
Windows Linux OSX
WindowID() HWND GtkWindow WindowRef
MenuID() HMENU GtkMenuBar / GtkMenu MenuRef
StatusBarID() HWND GtkHBox ControlRef (UserPane)
ToolBarID() HWND GtkToolbar HIToolbarRef
FontID() HFONT PangoFontDescription PB internal pointer
ImageID() HBITMAP GdkPixbuf CGImageRef
GadgetID() HWND GtkWidget ControlRef
ButtonGadget “Button” GtkButton / GtkToggleButton PushButton / BevelButton
ButtonImageGadget “Button” GtkButton / GtkToggleButton PushButton / BevekButton
CalendarGadget “SysMonthCal32” GtkCalendar HIView
CheckBoxGadget “Button” GtkCheckButton CheckBox
ComboBoxGadget “ComboBox” / WC_COMBOBOXEX GtkComboBox / GtkComboBoxEntry PopupButton / HIComboBox
ContainerGadget custom class GtkFixed UserPane
DateGadget DATETIMEPICK_CLASS GtkEntry UserPane
EditorGadget RICHEDIT_CLASS GtkTextView UserPane (rendering a TNXObject)
ExplorerComboGadget WC_COMBOBOXEX GekComboBoxEntry HIComboBox
ExplorerListGadget WC_LISTVIEW GtkTreeView DataBrowser
ExplorerTreeGadget WC_TREEVIEW GtkTreeView DataBrowser
Frame3DGadget “Button” / “Static” GtkFrame GroupBox / UserPane / Separator
HyperLinkGadget “Static” GtkButton UserPane
IPAddressGadget WC_IPADDRESS GtkEntry EditUnicodeText
ImageGadget “Static” GtkImage UserPane
ListIconGadget WC_LISTVIEW GtkTreeView DataBrowser
ListIconGadget “ListBox” GtkTreeView DataBrowser
MDIGadget “MDICLIENT”
OptionGadget “Button” GtkRadioButton RadioButton
PanelGadget “SysTabControl32” GtkNotebook Tabs
ProgressBarGadget PROGRESS_CLASS GtkProgressBar ProgressBar
ScrollAreaGadget custom class GtkScrolledWindow UserPane
ScrollBarGadget “SCROLLBAR” GtkVScrollBar / GtkHScrollBar ScrollBar
ShortcutGadget HOTKEY_CLASS GtkEntry EditUnicodeText
SpinGadget UPDOWN_CLASS + “Edit” GtkHBox containing others UserPane containing others
SplitterGadget custom class GtkVPaned / GtkHPaned UserPane
StringGadget “Edit” GtkEntry EditUnicodeText
TextGadget “Static” GtkLabel StaticText
TrackBarGadget TRACKBAR_CLASS GtkVScale / GtkHScale Slider
TreeGadget WC_TREEVIEW GtkTreeView DataBrowser
WebGadget custom class + ActiveX control GtkMozEmbed HIWebView
Notes:
  • Gadgets all have the same basic type noted in the GadgetID() row. However, Gadgets are devided into “window classes” on Windows, or subclasses of the general GtkWidget on Linux.
  • For Gadgets on Windows: If the entry is noted in “” then this is the class name, if it is not then this is the symbolic constant for the class name. You’ll have to look up the text value in the appropriate header files.
  • On OSX, there are no classes for the controls, its all a ControlRef. The names i noted here are derived from the control creation functions (ie CreateTabsControl()). If you are looking for information, you should start there.
  • Gadgets may have multiple classes depending on the flags on creation.
  • On Linux, many gadgets are placed inside their own container to add a frame or catch events. (GtkFrame, GtkEventBox). The GadgetID() command returns the real gadget in this case, not the container.
  • On OSX, the UserPane controls are drawn by PB itself, so you don’t have much ways to modify them other than the PB commands.

Thats it. I hope this information is useful to some people, and hopefully we will see some more cross-platform API examples on the forum in the future ๐Ÿ™‚

How we make decisions

Since we are on the issue of bug reports (see last post), here is how we made the decision to put “[Done]” in the title of fixed bugreports:

[12:32] <fr34k> this is the point were a bugtracker would be more helpful. there we could filter all solved bugs easily :)
[12:35] <AlphaSND> we already talked about that
[12:35] <AlphaSND> btw, we could change the title of the topic with [Fixed] in it, no ?
[12:35] <fr34k> true
[12:36] <AlphaSND> ok, let's do that starting from now
[12:36] <fr34k> thats a good idea actually, as often people keep posting after the "fixed", so it is no longer obvious from the last post
[12:37] <fr34k> what do we do with stuff that we determine not to fix? (not a bug, not solvable)
[12:37] <fr34k> should be marked as well
[12:37] <AlphaSND> yes
[12:37] <AlphaSND> we could put [Issue] -
[12:37] <fr34k> issue ?
[12:37] <AlphaSND> and move that in Coding Question
[12:38] <AlphaSND> Issue :p
[12:38] <AlphaSND> or [Feature] -
[12:38] <fr34k> lets just put [done] ... its shorter than [fixed] anyway :D
[12:38] <AlphaSND> [Done]
[12:38] <AlphaSND> ok
[12:39] <fr34k> [done] :p
[12:39] <AlphaSND> [Done] Damit !
[12:39] <AlphaSND> :p
[12:39] <fr34k> why ?
[12:40] <AlphaSND> because
[12:40] <fr34k> lets do it differently, so we see who fixed it :D
[12:40] <AlphaSND> man, let's be consistent :p
[12:40] <fr34k> berikco has to write [donE] :D
[12:40] <AlphaSND> :D
[12:40] <fr34k> i noticed, you also always write "Fixed" in the posts, i write "fixed."
[12:41] <fr34k> its not the beginning of a sentence, so there is no reason to write it capital. and its one more key to press damit :p
[12:42] <AlphaSND> It's a full sentence !
[12:42] <AlphaSND> Fixed.
[12:42] <AlphaSND> Uppercase, verb and dot
[12:42] <AlphaSND> :p
[12:43] <fr34k> lol, where have you been when grammar has been tought at school? :D
[12:43] <fr34k> coding probably
[12:43] <AlphaSND> :)
[12:44] <fr34k> now what? :)
[12:44] <AlphaSND> now what what ?
[12:44] <fr34k> i just fixed a bug, what do i write ?
[12:44] <fr34k> the rebell in me wants to write [done] :D
[12:45] <AlphaSND> [Done] -
[12:45] <AlphaSND> i'm the older here :p
[12:45] <fr34k> lol
[12:46] <fr34k> why the " - " btw ? 3 more keys!
[12:46] <AlphaSND> mannnnn !
[12:46] <AlphaSND> to differentiate of the original topic :)
[12:47] <fr34k> hey, i am just trying to optimze here
[12:47] <fr34k> "[Done] - 4.10 and 4.20 Beta 2 - Odd bug"
[12:47] <fr34k> "[done] 4.10 and 4.20 Beta 2 - Odd bug"
[12:47] <fr34k> so much better
[12:47] <AlphaSND> if you fix the bug faster than typing 2 two keys, ok
[12:47] <AlphaSND> the first one is indeed much more sexy ;)
[12:48] <fr34k> compromize: either the D or the "-" has to go ;)
[12:49] <AlphaSND> man, you're damn cra
[12:49] <AlphaSND> zy
[12:49] <fr34k> oh well, i have to go, food time. we'll continue this! :)
[12:49] <AlphaSND> let's remove the - then
[12:49] <fr34k> ha, success :D
[12:49] <AlphaSND> and eidt the 3 i did
[12:49] <fr34k> ok
[13:00] <AlphaSND> http://www.purebasic.fr/english/viewtopic.php?t=30825
[13:00] <AlphaSND> could you look at it, i can't find what's wrong with it
[13:21] <fr34k> re
[13:28] <fr34k> the mdi one is "[donE]" :D
[13:29] <AlphaSND> mouarf :)

In the end, i am glad we dropped the ” – “, because i am already having a hard enough time putting the “[Done]” in some of the bugreport titles. People use too descriptive titles in the bug forum it seems. Anyway, we are usually very much on the same page when it comes to the direction that PureBasic should take so big discussions are quite rare. But if we disagree, it gets tough! ๐Ÿ˜€

A Bug’s Life

Ok, its a cheesy title but it fits. I was asked to write about the process with which we handle bugs. The following is roughly how i handle this. I can’t speak for Fred but i don’t think the approaches are too different. This post is also intended as a guide on how to write effective bug reports about PureBasic, as the report can make a big difference here.

Each bug starts its life when it is posted in the bug reports section. If you want your bug report to be seen, write it in the (english) forums.ย  People sometimes try to tell me about bugs when they meet me on IRC, but this is a very bad idea. If i am unable to address the issue immediately the report will be buried in the log files and most likely forgotten. I am usually busy while i am on IRC so the chance to get something fixed immediately is very slim. Post it on the forum and it won’t be forgotten. Then we can discuss further details on IRC if you happen to catch me. The english forums is where i manage the bug reports. Anything reported somewhere else (IRC, email, german forums) starts at a huge disadvantage.

The first stage after a bug is posted could be called the “initial viability test”. I read every bug report that is posted the next time i visit the forums. I am not on the forum to specifically fix bugs at this point though so this is just a quick check. The relatively obvious non-bugs get filtered here. For example when the behavior is intentional or somebody confused a feature request with a bug. There are also a number of things that people often think are wrong where it is really just a lack of understanding (every few months somebody thinks GetCurrentDirectory() is wrong for example).

At this point i also run any provided example code if possible (if i have a copy of PureBasic in reach). This is why providing an example code to show the bug is very important even if it is just 5 lines of code and you think it should be plainly obvious. Its not that i am lazy. Remember that in this first stage i am not concentrating on fixing bugs. I am just browsing the forum in the lunch break for example. I am not going to write any test code at this stage, however if i just have to fire up the IDE and run it then i usually do that. When a bug can be reproduced at this stage, it starts its life with a big head start compared to the other reports because once i actually get around to fixing bugs i tend to start with these as i already have a place to start looking.

I usually do not comment the bug report at this point (unless it falls in one of the non-bug categories above) so if you post a report and do not hear anything for a while don’t be alarmed. I did read it and it will not be forgotten. Sometimes it takes very long for a bug to get actually fixed, sometimes it is done really fast. This varies a lot as do the reason for why some bugs take longer to fix. Sometimes we just don’t have a solution. Sometimes the solution would require a larger redesign of a library and is therefore postponed to when other planned changes to that library will be implemented. Sometimes the required work just outweighs the usefulness of the bugfix. In the later category i tend to give priority to bugs that cannot be easily worked around by the user. If a bug can be easily worked around with a few lines of code (but may be would be much more work to fix internally) then it may take longer than others. There is really no general rule here.

This brings me to the issue of “bumping” bug reports. Our usual response is “don’t bump” and sometimes it sounds a bit angry which some people might not understand so i will explain it a bit. To me bumping comes across as a bit rude and i am often very annoyed by it (hence the response). Its like saying “stop working on other stuff and fix my specific problem”. I can understand that a certain bugreport may be important to you and the lack of feedback from our side can be frustrating at times. But try to see the whole thing from my point of view: I am confronted with a large list of bugreports (from multiple OS) and i am trying to get them fixed as best as i can. My time is limited and i can only work on one thing at a time. It may be that i just spent a lot of time trying to fix one or more other bugs and then a comment like “still not fixed” or simply “bump” on a bugreport is just frustrating. I am well aware of the status of that bug because as i said above, i read everything that is posted in the bugs section. Bottom line: if a bug really is important to you and you think it should get more attention then ask nicely, don’t just bump. Explaining your reasons why it is important to you also helps, because if something is really a showstopper for you i may try to make an extra effort to get it fixed. In any case it gets you much further than just making me angry with a “bump” post, thats for sure.

Ok, back to the life of a PB bug. I do most of my bugfixing in larger batches at once (recently fixed 30 bugs for the OSX version in one weekend). You will notice this by a lot of [Done] appearing in the bugreport section. Sometimes Fred and I even have a kind of race about it to see who can dominate the bugs form (i own the OSX one right now obviously). For this i make a list of what i want to get fixed from the forum, sort it by priority and get to work on it. The method for fixing a bug is always the same, even for the very obvious ones: I first have to see the effects of the bug in action. Even if it is pretty clear from the bug description where the problem is in the actual code, i still have to make sure i can see whatever the problem is myself. This is the only way to really know that i fixed the bug afterwards.

This reproduction-phase is the most important (and often most difficult) one. Once again, if you provided a working code example that i could verify when i first read your report this problem is already solved, which is why i tend to fix such bugs first. If not then here is where the testing begins. You can make this alot easier for me by providing as much information as possible, such as the exact OS you are using. GUI related bugs are very often specific to some OS, or even to a specific set of settings. So include this information in your post.

My development PC runs Vista 64bit, but I also run VMWare and have a lot of different Windows versions set up for testing. The same thing goes for Linux: Linux distributions are so many and released so frequently that I usually cannot keep up with the latest release of the popular distributions. I am not somebody to install updates all the time. I do my development on OpenSUSE 11 right now. However, if you include your exact distribution and version, then i will download that and setup a VM for it in no time for testing (thanks to fast internet and VMWare this is really no big deal). With all this VM testing there is one common thing: If i have to test on a specific OS version, i most likely test in a clean install with just enough stuff installed to run PB. So if you have made any large changes (especially on Linux) from the base install which you might think could have an influence on the problem then please include that information.

The most important thing however in the whole reproduction-phase is to know what i am trying to reproduce. It may be plainly obvious to you what the bug you post about is, but it may not be to me, so always tell us what you think the problem is (don’t just post code). I have to know what i am looking for after all. It may also be that the bug does not appear with my testing configuration at all, so the code alone is no help here. Always include these two things in your report: What do you see when you run the code, and what do you think should happen instead. The clearer the report is the easier it is to work on it.

If i cannot reproduce it, i will post asking for more information or feedback from others. Here it helps if others respond with their results and also their system configuration. This usually helps a lot in figuring out bugs that are OS or configuration specific. I have seen posters who did not want any feedback from other users, probably because they feared that the existence of a workaround for their problem may diminish their chances of getting their problem fixed. It is the opposite actually: It would not be the first time that a posted workaround helped a lot in fixing a bug or implementing a new feature. Although it must be said that the existence of a workaround does not necessarily mean that it is a good idea to do it that way in the PB library. In any case, feedback on bugreports or feature requests is always welcome as long as it adds extra information. A simple “+1” is usually not helpful (except when the question was “can anybody else reproduce this”). A bug generally does not get fixed based on how much people provide feedback, but especially when we have trouble reproducing the problem then more information from others certainly helps.

If everything fails and i simply cannot get the bug to appear on any of my systems then things get tricky. I can do things like build special debug versions of libraries or the IDE so the user can help me track it down, but it involves a lot of back and forth. I try to avoid this if possible, butย  from time to time there is just no other way.

When the bug is successfully reproduced, it is time to hunt down the cause and fix it. There is really not much to say about this part. It really depends on the kind of problem at hand. It is usually a back and forth between modifying the PB code that shows the problem and the code of the library in question until the problem is found.

When the problem is finally fixed, i do the same steps/run the same code i used to see the bug in the first place once again to make sure it is gone. Depending on the kind of bug, i may add a routine to our unit tests (which are run by Fred’s build script before every release) to make sure it does not come back. I don’t do that too often though, as most of the times it does not really make sense. For example, a bug in any of the many GUI libraries cannot be automatically tested because they need user interaction. Also, many bugs are quite unique and not very likely to appear again, so unittests are a bit of a waste of time here. Its different for the compiler. Here it is much easier to have work in a different area cause bugs that were once fixed to reappear, so Fred maintains a set of unittests for many compiler features. Anyway, this is the end of the PB bug. Now its the obligatory “fixed.” post (or “Fixed” as Fred would write :)) and that is it. One more bug in the “[Done]”-category.

The cost of empty lists

So now we have the ability to embed lists, maps and arrays inside structures. Its a very useful feature and we can already see people building crazy data structures with all sorts of nesting in them, even tree like structures. I want to talk a little bit about the costs associated with these objects. The fact is, even empty lists or maps take up memory and if you create a lot of them, you should be aware of that. The sizes i give below are all for the x86 versions (so 4 bytes per pointer). For x64, you can usually double that. Not all fields are actually pointer sized, so it is less than that in reality, but it is a good approximation.ย  My base scenario is an array of a structure type with an embedded list. However, this applies to all other combinations as well.

Structure MyStruct
  Something.i
  List Elements.s()
EndStructure

Dim Table.MyStruct(99)
Automatic initialization on creation

First of all, its important to realize that structure content is automatically initialized on creation. This means that if you create a structured variable with an embedded list, you don’t have to call NewList in order to use the embedded list. However, this also means that simply by the process of creating the structured variable, you also created a (still empty) list which takes up some memory. The same thing applies to my example array: By creating the array with 100 elements (0-99), i also created 100 empty lists.

You could say this is a waste of memory, and you would not be entirely wrong. After all, if not all elements of this array are actually used to store data, that is a lot of unused memory. We discussed this at length when implementing the feature. An alternative approach would have been to leave all such dynamic elements uninitialized on creation and require the user to explicitly call NewList before using them in each element. This would conserve memory when not all array elements are intended to have items in their lists, but it puts the burden to ensure that all dynamic elements are initialized before they are accessed on the user and introduces a whole new source for bugs. Its also extra work for the user considering that if people define such a structure they intend to put things into it, and forcing them to do an extra initialization step each time when PB can easily do it for them is just wasting the programmer’s energy.

So in the interest of having a really easy to use feature we decided to go with automatic initialization. After all, if you really need the full control and do not shy away from some extra work then building your own linked data structures in memory is not that hard. If you want something simple that just works, then this feature is for you. Of course its never wrong to know about the costs so you know what happens when your data set gets large.

Memory usage

How much memory does an empty list take? Aside from the actual space it takes up inside the structure which is 2 pointers for a list (1 pointer for array and map), there is a header structure which is allocated as well. It stores information about the list such as its type (for sorting and cleanup), the pointers to the first and last element in the list as well as cached index information for a fast SelectElement() if possible. The list header is currently 48 bytes on x86. As described in another entry, we also have custom memory allocation for list elements for faster allocation/freeing of memory. These allocation routines also take up 24 bytes of header data per list.

So in my list in array example above i have created 100*12 bytes of actual element data in the array plus another 100*72 bytes for empty lists. While this extra data is still not very much, it is considerably more than the array element data itself, so it will become an issue if you make your array very large. If you knew for example that you will never have more than 18 elements in the embedded list, then you could replace the thing with a static array inside the structure with 18 elements and still have the same memory usage.

Empty maps have a much larger impact. They also come with a header and an allocator header, but an empty map also reserves space for its hash table even if there are no elements in it. The default table size is 512 entries, so that is another 2048 bytes for an empty map on x86.

Arrays are a little different as you can declare inside the structure how many elements it should have on creation. Arrays also come with a header, but nothing else. Here its more important to keep in mind that if the elements of the embedded array in turn contain embedded lists, maps or arrays that you have to take their costs into account as well as they will be initialized too when the embedded array is created.

One final thing to keep in mind is that lists and maps actually allocate the memory for their elements in blocks of 16 elements minimum (for speed and to reduce fragmentation), if you create many lists with just a few elements in them (say 2 per list for a binary tree structure), you end up with used memory of 16 elements per list instead. Note though that this is just allocation, not initialization. The used memory will be only for the 16 elements themselves, not for any contained dynamic list, map or array they may contain.

Bottom line

This post should be seen in the right context. The cost of the embedded object may seem large compared to the size of the structure they are put in, but that doesn’t change the fact that they are still very cheap objects. Lists, maps and arrays are all memory-only objects. This means that all they need is memory, no handles, no system objects, nothing else. Memory is not much of a concern these days, so you can create many of them and even create/destroy them frequently. This really isn’t a problem. Take my above array example: Even if we Dim the array to 10000 elements, we still only used up 820Kb of memory which is not much by today’s standards.

The only place where you really need to concern yourself with this is if your data sets get really large and memory does become an issue. In this case the advice is simple: Avoid “sparse” data structures where a large part of the dynamic elements are allocated but not used. You can also call FreeList(), FreeMap(), FreeArray() or ClearStructure() on any element you do not need. You then have to re-initialize it if you want to use it later with NewList, NewMap, Dim or InitializeStructure() respectively.

Always keep in mind the quote from Donald Knuth on optimization:

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified.

In this context, this means not to worry about this issue when designing your data structures. Do not get tempted to optimize away a few bytes of extra memory with funny tricks. A clear and simple layout of the data in your program is worth much more than that (and will save you many headaches from chasing bugs). Only if you see that your program really has a memory problem is it time to start thinking of ways to cut down on overhead like this.

In this spirit, have fun designing crazy nested data structures with the new dynamic elements! ๐Ÿ™‚

Purify it !

What a wierd title. Or may be not. Depending of your experience, you may or may not know these life-saver tools, when strange things happen to your program and you have absolutely no clue about what the hell is going on. No starting point to look at. It sometimes happens, sometimes not, as if the computer was an organic lifeform. Here enters purify like programs (‘valgrind’ on linux, ‘purify’ or ‘bound checker’ on Windows) , which adds realtime checks around almost every kind dynamically allocated memory block to detect the root of evils: buffer overflows.

You may think: “well that’s cool but i use PureBasic, and barely never dynamically allocated memory block, as all is managed by natives libraries”. You are right. But may be you use pointers, or even OS calls. And this is where things got a bit more complicated, as pointers is the ultimate power in programming. And as all power, it has to be used wisely. Are don’t get me wrong: you will use them wisely. Until something is changed in your code which impacts that and suddenly the pointer gets crazy, without any notification. On a big program, it could be very long to isolate the bug. I have some ‘souvenirs’ of very vicious cases which took me several days to fix. And as PureBasic executables uses a custom debugger format, it is not compatiable with any of the purifier tools. Too bad. Until now.

Before going further in the details, let’s try to really understand how a purifier works. Basically, it puts ‘cookies’ around every potientally dangerous code items: variables, arrays, strings, dynamically allocated memory block. What’s a cookie ? It’s just a predefined value (usually 32 bits), often something funny like $BADBEEF or $BADF00D. At every executed line, the purifier (which holds a list of all the installed cookies address) will check them, and if one is broken will stop. That means than something has erased this area which should never have been erased. Ever. That’s a bit like a code integrity check. As we could imagine, the bigger your program is, the slower the purifier will be. And it can be very very slow, that’s why it’s possible to tune it to check only some kind of data, and at which rate.

Consider this code:

Procedure a()

a.l = 10

*CrazyPointer.Long = @a
*CrazyPointer+1
*CrazyPointer\l = 152ย  ; stack corruption

EndProcedure
a()

This is an obvious error. But it does go unnoticed when running in standard release/debug mode. On some more retricrive OS, it could even crash. If you activate the purifier it catch it immediately and report a stack corruption. Let’s try another less obvious one:

*LocalDrive = AllocateMemory(#MAX_PATH)
GetModuleFileName_(0, *LocalDrive, #MAX_PATH)

This snippet uses Windows API, and get back the executable name. So far so good. Now what happen if you change the program mode from ASCII to Unicode ? You end up with a buffer which is twice smaller as requested, and chances are high than you will be hit with a nasty buffer overflow. Again the purifier will detect it as the ‘cookie’ put at the end of the allocated memory block will be overwritten.

And there is quite some cases where it can happen: a structure too small to recieve the expected data, wrong associated structure to a pointer, pointer manipulation error, wrong API call, etc. For most of them, the purifier will catch the error. There are some limitations tough: threaded program could report wrong line andย  if a pointer is really filled with a random value, it won’t be noticed by it (but probably by the debugger as IMA – Invalid Memory Access).

The purifier is now available in PureBasic 4.50 !

PureBasic 4.50 Beta 1 released!

Hello everybody!

… this is not an april fools joke… or is it? ๐Ÿ˜›

We are proud to announce the first public beta of the upcoming PureBasic 4.50 release. As promised, the release cycle is much shorter than that of the 4.40 release which means that also the feature list is shorter. Nontheless, some long requested features have been implemented in this release which we hope you will enjoy very much.

The most notable are:

  • Support for Array, List and Map in structures:ย  These can be nested as much as you want, so you can have a Map-in-List-in-List if you want to. There is no need to call NewList, NewMap or Dim on these elements. They are created as soon as the outer structure is created. Dim can of course still be used to change the size of a dynamic array. Arrays inside structures can only have one dimension for the moment. The debugger has full support for this too. Embedded Arrays, Lists or Maps can be easily viewed by right-clicking on them in the Variable Viewer.
  • Image Library changes: We decided to abandon the support for images with depths below 24bit. Support for images with a palette was Windows-only anyway and had quite a number of bugs as well. The library now stores images internally only in 24bit or 32bit format which makes things a lot simpler. Images can still be loaded (and now also saved) at lower bit depth, so you can still work with them if you need to.
  • IDE Improvements:ย  The ability to select the used compiler in the compiler options allows to easily switch between different versions from the same IDE. It also allows to easily build and debug 32bit and 64bit applications from one IDE. Furthermore, some longer requested options like keyword sensitive indentation and indent guides have been added.
  • Debugger improvements: There is a brand new ‘Purifier’ tool in the debugger. It adds special ‘cookie’ values around variables, strings and allocated memory blocks to detect when the program accidentally writes past its intended target buffer. As this requires support from the compiler, it has to be activated in the compiler options to be available in the debugger. Furthermore, the already discussed network support and data breakpoints are now available.
  • Up to date documentation:ย  The english help file has already been updated with all documentation for these new features. The other languages will follow in the final release.

The feature list:

PureBasic 4.50 Beta 1
- Added support for Array, List, Map inside structures
- Added CopyList(), CopyMap(), CopyArray() commands
- Added FreeList(), FreeMap(), FreeArray() commands
- Added CopyStructure() and InitializeStructure() commands
- Added volume support to PlaySournd()
- Changed: The Image library now keeps images only in 24bit or 32bit (loading and saving works with other bit depths)
- Added Depth parameter to SaveImages()ย  (default is the original depth when the image was loaded)
- Added ImageDepth() flag to get the original or current image depth
- Added #PB_Image_Transparent flag for CreateImage()
- Added 32bit support to TGA image decoder
- Added 32bit support to BMP image encoder
- Added RoundRect() command to the 2DDrawing library
- Added #PB_2DDrawing_AllChannels mode for DrawingMode() (modifies all channels without blending)
- Added image support for the ComboBoxGadget command (not supported for editable ComboBox on Mac OSX)
- Added AbortFTPFile()
- Added graphical console functions to linux
- Added large file support to File lib on Linux/OSX
- Added RandomData() command
- Added CryptRandom(), CryptRandomData(), OpenCryptRandom(), CloseCryptRandom() commands
- Added many more Math functions: Exp(), ATan2(), Radian(), Degree(), [A]CosH(), [A]SinH(), [A]TanH(), IsNaN(), IsInfinity(), NaN(), Infinity()
- Added 'Debugger' Library to control some debugger actions from code

IDE/Debugger:
- Added Keyword underline for Break, Continue, ProcedureReturn
- Added StatusBar help for prototypes and interfaces
- Added Keyword sensitive indentation (block mode is still available)
- Added "Format indentation" option in the edit menu
- Added indentation guides and whitespace options
- Added the ability to select multiple compilers in the compiler options
- Added Purifier tool for the debugger
- Added full debugger compatibility between all OS and processors
- Added network debugging for the standalone debugger
- Added data breakpoints for the debugger
- Added maximize button to Variable-, Memory-, Library Viewer and Callstack
- Added support for structured items in the 'View Array/List/Map' tab of the Variable Viewer
- Changed: The Array, List or Map name in the Variable viewer should be entered with a "()" now to display their elements.
       (It is automatically corrected if the () is missing)

As always thank you to everyone who helps test these beta versions and reports bugs. Have fun with this new version and tell us any problems that you have. As usual, this version can be downloaded on your personal account on http://www.purebasic.com/

Oh, and Happy Birthday Fred!ย  :mrgreen:

The PureBasic Team

A little bit of history about the debugger

In the last entry i mentioned that the debugger is compatible between 32bit and 64bit (you can debug a 64bit program with the 32bit debugger and vice versa). Why is that and why do we have 3 different debuggers anyway?

Its interesting actually: a lot of these things were not planned to be like that. In fact the whole debugger was not planned at all. It was one of these things that start as a simple idea and just grow into something huge. To understand more about that we have to go all the way back to when the current debugger was written. It was the year 2004, the current PB version was 3.91. The Windows debugger consisted of a small window which only showed the global variables and allowed stepping through the program. PureBasic for Linux was much worse. There was no IDE at all and the debugger did notning more than print the output of Debug statements to the command line.

At that time i decided to write the new IDE. Thats another project that vastly outgrew the original plans even on its initial release. The plan was to write a simple crossplatform IDE with the same set of features as the IDE for Windows at the time. Most of the IDE was working and we planned to include it in the 3.93 release. All it did for debugging on Linux was to make sure the program runs in a terminal window so you can see the debug output. To improve that, Fred gave me the source to the Linux debugger lib (just a collection of printf statements) so i could improve it.

The Windows debugger was just a library that got compiled into the executable which launched a new window in a thread. The idea was to do the same for Linux and just duplicate the feature set of the Windows debugger. Man am i glad that this didn’t work out! If that worked, we would probably be stuck with two very platform specific debuggers with probably a similary limited feature set as we had back then, and maybe no OSX debugger at all. Well, it turns out that Gtk doesn’t play so well with threads, especially when the main program doesn’t even know about the debugger thread.

New plan then: the debugger had to be an external program. Usually debuggers get access to their target programs by reading their memory directly, even patching the target program’s code as needed. Since this approach is quite platform specific we decided against that. After all, if we write a new debugger it should be available for Windows as well. So we still have the design of a special debugger library which gets compiled into the executable even today. This library allows the debugger to connect via pipes to exchange the needed data. This design requires a minimum of platform specific code and it works quite well.

Now we come to the reason for the three debuggers. The console debugger exists because I started this work on Linux with the given minimalistic library that just printed the output. This just grew to be the console debugger. To ease the testing i first implemented a new feature such as array display in console mode to make sure i got all the interaction with the compiler generated tables and stuff like that right and only as a last step i also added a GUI version of that. I still do it the same way today. So the main purpose of the console debugger is the development of the debugger itself. I also use it a lot to debug the IDE because I do not want to load that beast into the gui debugger every time.

I almost didn’t write the standalone gui debugger. After all, why add an extra window when you can access all the features from the IDE. The only reason i did it was to not kill the jaPBe project. Without any way to debug a program other than the IDE, people would have abandoned it fairly quickly. I was tempted though. After all, it would have been an easy way to get rid of the “competition”. ๐Ÿ™‚ Looking back, the standalone debugger has proven useful in many more situations than that. For example this way programs can be debugged with elevated privileges on Vista or 7 without the need for the whole IDE to run at that level. Also it has again proven helpful in the development of the IDE and debugger itself. The codebase of the gui debugger is much smaller than that of the IDE so it is much easier to find a bug in there. Since most of the code is shared with the IDE we get that fixed as well.

The three debuggers also help in porting to a new platform like with the x64 port. As soon as the compiler works at least at a minimum, the first order of businesses is to get the console debugger up and running. This is then used to further debug the compiler output. When that works reliably enough the next step is to get the gui debugger and IDE running. Getting the IDE to run is quite the milestone for every port, as it is quite huge and makes use of a lot of different libraries.

Back to the communication between executable and debugger. As i said, it works through pipes. Its a simple package based binary protocol that sends a header structure and then data with a variable length. It hasn’t changed much since the first version although the code for handling the pipes has changed quite a lot. With the 64bit port we faced the question what to do with this communication protocol. Changing all values to 64bit would have been rather simple and the obvious choice. The reason why i choose not to do it was again for development purposes. The 64bit version was still quite unstable and having a stable 32bit debugger there to analyse it was a big help. At one point i was running the 32bit gui debugger to debug the 64bit gui debugger while debugging a 32bit executable. ๐Ÿ™‚

So this is why the debuggers are compatible. The communication protocol keeps its 32bit header structure and the debuggers can adapt to the executable’s bit size when working with the contained data. Now with the 4.50 this design pays off once again because you can now actually load the 64bit compiler into the 32bit IDE for compiling and debugging. The final step was to make the PowerPC and x86/x64 communication compatible. This is something i just did recently because its a bit more of a change because the different byte-order has to be taken into account and so far there was just no need for this. Anyway, i did not do all this work just so you don’t have to use Rosetta on the IDE on OSX. Now that the debugger communication is compatible between all platforms, the way is finally open for one of my longer term goals: network debugging across all platforms.

Now with the next version we will have that as well: Network debugging across all platforms and processor types. I recently ran the debugger in a 32bit Linux VM, debugging a PPC executable on the Mac, while itself being debugged by another debugger instance on 64bit Vista. That just rocks! ๐Ÿ™‚

To all those not really interested in this kind of stuff: you benefit from this as well. All these steps to new platforms and crossplatform compatibility have made the debugger more stable as a whole too, because many bugs become more apparent when you switch platforms frequently. So everybody wins.

PureBasic 4.41 final is out

Hello folks,

The final version of PureBasic 4.41 is out. It’s a stabilisation release, so we focused only on bug fixes and we have killed quite some :). You can grab it on your online account while it’s hot !

Have fun,

The Fantaisie Software Team.

PureBasic 4.41 RC1 Released!

Hello everybody,

The next release is here. We spent quite some time fixing bugs, so here is the 4.41 bugfix release. As you can see it is called “Release Candidate”. I will explain what the different types of releases mean for PB now:

  • Alpha Release:ย  These are development snapshots and are not given out to the public. Features are usually not fully finished. This is just to get some early feedback from a few people during development.
  • Beta Release:ย  Beta means that all new features are implemented and nothing new will be added except bugfixes (except if a feature is found to be too incomplete to be usable).
  • Release Candidate:ย  This is what used to be the “final beta release” before. In the release candidates we plan to fix only very critical bugs. Minor bugfixes will be postponed to the next version. We release them when we think the version is ready. At this point even people that do not try the betas should test the new version as its the last chance to discover a critical bug.ย  This release starts directly as a release candidate as there are no new features to test and we only need the validation that we did not break anything with our bugfixes before going to the final release. Releases that introduce new features will have a beta phase as usual.

We also plan to do these bugfix releases (like 4.31 and 4.41) in between new feature releases on a regular basis from now on, so if you are not interested that much in new features and want the most stability then you can just skip the new feature releases and use the bugfix releases only. This way the new features have been tested for a while and bugs in them have been fixed. We also plan to try to shorten the release cycle as the 4.30 and 4.40 release cycles have been way too long.

Please test the new version and tell us whether the bugs marked as fixed are indeed gone and also let us know if any new problems show up. As usual this release can be downloaded on your account on http://www.purebasic.com/

The PureBasic Team