Issue 3-26, July 1, 1998

Be Engineering Insights: Getting Your Translator Add-ons to Use the Translation Kit

By Jon Watte

One of the many good things in BeOS Release 3 was the addition of system-wide translation services through the Translation Kit. While translation between different data formats was previously available in the form of a third-party library named Datatypes, having a Kit in the BeOS makes it easier to use and install, because you can assume it's always there. The Translation Kit will load any old-style Datatypes add-ons, but the interface used by Datatypes is deprecated.

The actual work of translating data in the Translation Kit is performed by add-ons known as translators. This article explains what a translator add-on must do to be used by the Translation Kit, and what it can do to be a well-behaved citizen in the world of data format conversions.

But first, the code! The archive can be found at:

The purpose of our translator is to allow applications to read and write the PPM bitmap file format. I chose PPM because it is a format that is fairly simple to understand, while having enough variation to illustrate how to configure a translator add-on. It is also a fairly popular format for UNIX-style image processing tools.

For translation to work, there has to be some common ground between the translators, and the applications using them. For bitmap graphics, this common ground is found in the B_TRANSLATOR_BITMAP format, a format designed to be easily readable and writable to BBitmap system objects. The format of a B_TRANSLATOR_BITMAP formatted file is simple:

First, there is the file header, which consists of a struct:

struct TranslatorBitmap {
  uint32       magic;    /*  B_TRANSLATOR_BITMAP  */
  BRect        bounds;
  uint32       rowBytes;
  color_space  colors;
  uint32       dataSize;

As you can see, all elements of this struct are 32 bytes in size (except for the BRect, but all elements in a BRect are 4 bytes in size) so there should be no alignment problems when reading/writing this struct on different platforms. However, the byte order needs to be well defined, and since Datatypes was around long before the x86 port of BeOS, the well-defined byte order of the TranslatorBitmap struct is big-endian.

The magic field should be set to the B_TRANSLATOR_BITMAP constant, swapped to big-endian if necessary.

The bounds field should be set to the Bounds() that a BBitmap system object would use to contain the image. Note that Bounds().right is ONE LESS than the width of the image in pixels, because the 0-th pixel counts as one pixel. Again, you need to swap the BRect members as necessary.

For rowBytes, see below.

colors is one of the values defined in GraphicsDefs.h which describe various ways you can interpret the raw pixel data. In R4, there will be more values defined for a color_space, although not all values work if you use DrawBitmap() to draw such a bitmap to the screen. In the sample source, we have cribbed the definitions for B_CMYK32 and relatives from R4 so that we can illustrate how to convert between color spaces.

dataSize, lastly, should tell how much pixel data follows the header, but the size of the header (32 bytes) does not count. This should always be set as follows:

header.dataSize = (header.bounds.Width()+1)*header.rowBytes

Again, be careful about byte-swapping.

After this struct the actual data of the bitmap follows directly, from left to right, top to bottom, padded to rowBytes bytes per scanline. rowBytes is typically the smallest multiple of four bytes that will fit the width of the bitmap of whole pixels across.

The general rule with regards to byte-swapping is to swap only when you need to read or write data, and keep it in the native format internally. Doing this ensures that you can easily access the values of the header. For instance, if you were to write a BBitmap out in the B_TRANSLATOR_FORMAT format, here's how you could do it:

status_t WriteBitmap(BBitmap * map, BDataIO * out)
  TranslatorBitmap header;

  /* prepare header */
  header.magic = B_TRANSLATOR_BITMAP;
  header.bounds = map->Bounds();
  header.rowBytes = map->BytesPerRow();
  header.colors = map->ColorSpace();
  header.dataSize = header.rowBytes*(header.bounds.Width()+1);

  /* swap header */
  header.magic =
  header.bounds.left =
    B_HOST_TO_BENDIAN_FLOAT(header.bounds.left); =
  header.bounds.right =
  header.bounds.bottom =
  header.rowBytes =
  header.colors =
  header.dataSize =

  /* write header */
  status_t err = out->Write(&header, sizeof(header));

  /* write data */
  if (err == sizeof(header)) {
    err = out->Write(map->Bits(),
    if (err == B_BENDIAN_TO_HOST_INT32(header.dataSize)) {
      err = B_OK;
  return (err > B_OK) ? B_IO_ERROR : err;

I have sloppily been saying "file format" above; the truth is that any BPositionIO object can be used by a translator, and as long as you can Seek() and SetSize() and Read() and Write() it, it needn't be a BFile proper. It can be one of the system classes BMallocIO or BMemoryIO, or it can be your own class that knows how to read and write data to some special storage you're using. This is used by the system class BBitmapStream, which knows how to present a BBitmap as a "stream" of data.

Now, your job as a bitmap image translator is to read data in your "special" file format from the input stream, and write it to the output stream in the "standard" bitmap format as explained above. You should also be capable of doing the reverse: reading data in the "standard" bitmap format, and writing it out in your special format. This reading/writing is done in the exported Translate() function.

Translate() is passed an input and output stream, type information that a previous call to Identify() returned, possibly a BMessage containing some configuration information and out-of-bounds information, and a requested output format type. This type is a four-letter type code as found in and other system headers, and the specific value is taken from your outputFormats[] array or the return data from Identify(). If there is no type code defined for the format you're dealing with, you have to make one up. When you do, remember that Be reserves all type codes that consist solely of upper case letters, digits, the underscore character and space. Your best bet is to use lowercase letters in your own type codes.

There are standard formats for some other kinds of data besides bitmap images. You can find them in TranslatorFormats.h, and they are also described in the Translation Kit chapter of the online Be Book.

There are some things that you need to get the Translation Kit to the point where it calls your Translate() function. There are many translators installed in a typical user's system, so how does it know which translator to use? Typically, a translator is selected in one of two ways:

1) An application that implements an "export" menu item, such as the Becasso paint program, or the R4 version of ShowImage, calls on the Translation Kit to list all available translators, and to select those that say that they can translate from the B_TRANSLATOR_BITMAP format to some other format. It then lets the user choose one of these translators using some UI (a dialog or menu, typically) and tell the Translation Kit to especially use the translator selected.

For this to work, your translator needs to tell the world what formats it can read and write. It does so in the inputFormats[] and outputFormats[] arrays. These are arrays of struct translation_format, terminated by a format with all 0 values. While exporting these arrays is called "optional" in the documentation, applications that want to perform an export will not know about your translator unless it exports these arrays.

Also note that there is no way to specify that only certain combinations of input and output file formats are good. Once you declare some input formats and some output formats, any combination of them may be used by the Translation Kit, including, in some degenerate cases, translating the SAME format (i e B_TRANSLATOR_BITMAP to B_TRANSLATOR_BITMAP). You decide how to best deal with this situation; just copying >from input to output is acceptable, although if your translator can also do some other tricks (like the color space conversion of PPMTranslator) you might want to do that even on same-format translations.

2) An application that accepts "any file" and then uses the Translation Kit to figure out what it was will cause your Identify() function to be called. The role of your Identify() function is to look at the beginning of the file and figure out if it is in one of the formats you know how to handle or not. Note that Identify() is often called before Translate(), even if the application selects your translator specifically, so you have to do a good job here.

Because the BPositionIO passed for input may have some special meaning, such as reading from a network socket, you should not read more data than you need to make an educated guess as to the format of data you're passed. Similarly, calling Size() or Seek() relative to the end-of-file of the BPositionIO might be an expensive operation that causes the entire file to be downloaded to disk before it returns, so it should be avoided in your Identify() function. If not, you might not recognize the format, and then the user wasted an hour on a 28.8 kbps download just to get nothing useful out. Also, some applications use the Translation Kit only to identify what something is; they don't actually Translate() it. Wasting time getting to the end of the file is then doubly pointless.

There are some additional required data items you need to export from your translator for the Translation Kit to use it. They tell the world your translator's name, some information about it, and the version. If there are two translators with the same name but different versions installed, the Translation Kit may choose to use only the latest version, for instance. Thus, you should make sure that you always bump the version number when releasing a new version of your translator, and that you never change your translator's name (as seen in translatorName[]) once it's set.

translatorInfo[] is your personal soap box, and is a great place to put shareware notices, copyright information, URLs, or secret cabal messages. Except that then they wouldn't be secret anymore.

There are three more optional functions that you may choose to export, even though your translator will work and be used by the Translation Kit without them.

MakeConfig() allows you to create a BView (to which you can add other BViews such as BCheckBoxes and BMenuFields) that a client application can add to a window and display on screen. The purpose of this view should be to twiddle whatever tweakable parameters your translator has, and the View should remember these changes for later uses. You can see this implemented in the PPMTranslator as the struct ppm_settings variable g_settings, and the PrefsLoader class instance g_prefs_loader.

GetConfigMessage() should return a "snapshot" of the current settings in the message passed to it. An application can pass a copy of this "snapshot" message data to a later call to Translate(), and your translator should then use whatever settings are kept in that message rather than the defaults. Similarly, an application can pass a copy of the data in this message to MakeConfig() to have the view preconfigured to the settings stored in that message rather than the current defaults (although the translator is allowed to change the defaults to what's in the message, as done in PPMTranslator).

These two functions together make it possible to create an application which can present a UI for choosing a translator, to configure that translator, and later to use that specific translator/configuration pair to actually perform a translation. Great for automated batch conversions, for instance!

For more detailed information on the functions used by the Translation Kit, look at the Translation Kit chapter of the Be Book, the section on writing a Translator add-on.

The last optional function is main(). On the BeOS, there really isn't any difference between shared libraries, add-ons, and applications, except in the way they're used and what you call them. You can load an application as an add-on, or launch a shared library, providing that the executable in question exports the right functions. To be an application, all you have to do is to export a symbol named main().

PPMTranslator takes advantage of this schizophrenia to do something useful when double-clicked—it runs its own control panel by calling its own MakeConfig() function and adding the resultant View to a window, and then quits when the window is closed. I recommend that all translator add-ons do the same thing; that gives a user an easy way of setting the defaults for use by applications that don't display translator user interfaces, and users also get something useful out of double-clicking what might be an unknown executable found on their disk.

Once your translator is debugged and ready to ship, you only need to make sure it gets installed where the Translation Kit will find it. By default, the Translation Kit will look in the following three places for Translator add-ons:

/boot/beos/system/add-ons/Translators/—reserved for Be
/boot/home/config/add-ons/Datatypes/  —for old Datatypes

However, the user can change this behaviour by setting the environment variable TRANSLATORS. Users who do this are considered power users, so making sure your translator gets installed in ~/config/add-ons/Translators/ by default is the right thing to do.

Before I end this article, I want to explain a few things about the code included with this article.

First, there is downloading and installing the code. Just get it from the URL above, put it where you usually put sample source code, and un-zip it (or let the Expand-o-matic do it for you). Then, in a Terminal window, "cd" to the newly un-zipped folder, and type make install to build and install the PPMTranslator and the translate command-line tool. Documentation for the use of translate is scarce, but you have the source, so you should be able to figure it out from there.

PPMTranslator should be doing most things "right" and thus be suitable as sample source. If you find something you don't like or think might be a bug, I'd be interested in hearing about it, and fixing the archive.

The utilities in colorspace.cpp are intended as a quick way to get the job done when you need to output data in some color_space format other than what you have. They are not intended as a high-quality color convolution or separation package. Specifically, the conversion to grayscale is sub par, and the conversion to/from CMY(K), while correct, assumes that you're using perfect inks on perfect paper. I wish!

If you read through the sources and conclude that Release 4 will define new values for the color_space enum for color spaces not previously defined in , you are correct. However, there is one caveat: while using this enum to communicate color space information is convenient, not all applications or classes will support all color spaces. Drawing a BBitmap in the B_CMYK32 color space to a BView will not work; nor can you draw into a BBitmap with a color space of B_YCrCb_422. Still, having names for these spaces is better than a complete vacuum.

What are you waiting for? The source is there to explore, and the world is waiting for your translators! Shoo!

Be Engineering Insights: How to Avoid Engineering Pitfalls That Defeat Global Sales

By Lynn Fredricks

Your programmers work 24-hour days to get your product ready for domestic sales. But, although 50% or more of your revenue is on the line, you don't think to ask them to test the product under the German or Japanese version of the operating system. It's only after your product starts appearing on shelves at CompUSA that the Vice President of Worldwide Sales asks you to demonstrate this hot application running under the Japanese operating system for a group of high-ranking executives from Tokyo. After the fourth crash and reboot, you realize that something isn't right with your product. Your VP apologizes, then says goodbye to the high- ranking executives and to 30% of worldwide revenue.

For most development houses with an international presence, international sales account for upwards of 40% of total revenue. These revenues, however, can be severely reduced or eliminated for any software release when products aren't engineered for sales in international markets. In effect, your engineering department determines your global sales strategy, which may well end up defeated by or suffering from long and expensive localization processes because software products have been written with only a single target language in mind.

Before localizing your software, you need to internationalize it. Internationalization is the process of creating a single code tree that is easily localized to multiple languages. Before spec, during development, and through final code freeze, your engineers should be well-versed in writing international software. Here are five of the most common pitfalls that cripple or defeat the international value of software products.

Language Code Incompatibility

English letters are usually presented as single-byte characters. Japanese, Chinese, and Korean characters are double-byte. Some text can be represented vertically. In the Middle East, some languages flow from right to left. All platforms have API calls to display all major languages, including US English. A program can automatically display date, time, and other culture standards by querying the operating system. Make sure your programmers learn and use these APIs wherever characters are displayed.

Third-Party Components

Some components may not be localizable. A third-party vendor may not license a component for international distribution, or may demand additional licensing requirements and fees to localize it for you. Before a component is even tested for use in a product, require a sign-off for worldwide rights and compatibility with all international versions of the OS your company targets.

Hardcoded Resources

Your programmers always keep pieces of text, pictures, and sounds in a separate, editable resource file, right? If resources aren't kept separate from the start, your programmers will either have to re-engineer later, or comb through your source code every time you localize into another language. Separate resources are relatively easy to edit, reducing engineering costs considerably, or allowing you to outsource localization entirely.

Non-Existent SDKs

Almost every SDK starts as English-only. Even Sun's Java Development Kit only recently added true international support. If your product is dependent on a third-party SDK, ensure that it is either compatible with targeted international operating systems, or that you are absolutely sure that international versions will be available by the time you are ready to localize.

Non-Standard Docs and Help

Tech writers and help authors like to adapt as much as possible to the platform they are writing for, and add improvements with incremental releases. Usually, help systems, as well as electronic documentation systems and players, do not exist on all platforms and language versions, increasing development costs for these systems. Every new or different word in the documentation has to be translated, copy-edited, and re-published. Therefore, it is practical and cost-effective to standardize on cross-platform, open systems, and to enforce a company policy of standardized product terminology.

International success depends not only on the savvy of your business development team, but on a cooperative and proactive understanding of how to create an internationalized product. Being aware of the pitfalls and integrating strategies into your product specifications will allow you to realize your global revenue potential.

About The Author

Lynn Fredricks is the President of Proactive International, an international business development company that establishes worldwide distribution networks for software developers. Proactive International specializes in high-profile distribution between North America and Japan, as well as providing product and business analysis for large international corporations. Additional information can be found at

Developers' Workshop: Putting Pen to Paper

By Michael Morrissey

A while back, Stephen Beaulieu mentioned that DTS has divided support responsibilities for the various areas which make up the BeOS, based on familiarity, experience, and preference. The areas which fell towards me include OpenGL®, POSIX, Replicants, hardware, and printing. When I think about what I'd like to write for my Newsletter articles, I look into the questions which have come my way recently to see what people are asking about. This article was going to be on OpenGL®, but people asked about printing last week, so that's what we'll look at. OpenGL® will have to wait until my next article!

Printing is a great topic, because it produces a physical representation of your work. It's very validating. You can hang your hardcopy on your office wall, show it to your friends, and just generally impress people with it. Portable, high-contrast displays are wonderful, but printing will always be valuable. As I once heard it put so eloquently, the paperless office is as much a myth as the paperless bathroom. (There's a very subtle double entendre in there, "for the connoisseur" as Jean-Louis might say.)

Benoît Schillings wrote an excellent article...

Be Engineering Insights: Proper Printing

...a short while back which explained how to get up and running with printing on the BeOS. I want to expand on that article just a little, showing a couple of techniques which you may want to incorporate into your own applications. The code we'll be adding printing to is none-other-than Dynadraw, the perfect vehicle for showing off printing. You can grab the source code from:

In printing out Dynadraw views, it would be nice to have two modes: one in which one pixel on the screen corresponds to one typographical point on the page, and another where the entire view is scaled to fit on a single page. Correspondingly, under the Printing menu, you'll find the item Scale to Page which has a check next to it. The check implies that we'll scale the view to the page, and the absence of the check implies that we'll produce "life size" output.

The DDWindow class manages the printing, and is the target of the two new messages, PRINT_REQ and SCALE_PAGE. SCALE_PAGE notifies the FilterView that we're toggling printing modes. PRINT_REQ calls the imaginatively named DoPrint(), which sets up the print job, requests the view to draw in the correct position, spools pages, and commits the job.

The most important thing to do when creating a print job is to set the print settings. This is usually done by making a call to BPrintJob::ConfigPage(). These settings are stored in a BMessage, which you can get a pointer to by calling BMessage::Settings(). It is extremely instructive to view the contents of this message when investigating or debugging by calling PrintToStream() on the message—try uncommenting these lines in DoPrint() and then running the application from a Terminal window:

  printf("printSettings message:\n"

Next we calculate how wide and how long the printout will be. We do this by taking the ratio of our view width to our printable rectangle width, and rounding up with the ceiling function. The total number of sheets, therefore, is horizontal sheets multiplied by vertical sheets. We then loop through the pages, offsetting the current page rectangle to the top of the next page.

With the current page rectangle lined up correctly, we call BPrintJob::DrawView(), which calls FilterView::Draw() with the rectangle described by curPageRect, and positions the output at (0,0) on the page. Having drawn, we add the page to the spool file, check that we haven't been interrupted, and continue the loop. Finally, we commit the job, and send the spool file to the printer.

The FilterView::Draw() function needs to be modified only slightly. If the view is being printed and the user has selected Scale To Page, then we determine a scaling factor and apply it. We determined the scaling factor here by finding the horizontal scaling factor and the vertical scaling factor, and taking the smaller of the two. We call SetScale() with this argument after reducing it by an epsilon, to make it more attractive on the page. (Note that SetScale() takes a double, with 1.0 == 100%, that is, full size.)

The BView::IsPrinting() function is critical to any application which does printing. It allows the Draw() function to modify its behavior for the printed page versus the screen. Any "screen-only" code you have in your draw function should be wrapped in an "if (!IsPrinting())..." check.

As you can see, the BPrintJob class and the BView class work hand-in-hand to allow you to add quick and easy printing to your applications. Read Benoît's article, read about BPrintJob in the Be Book, and try it yourself! Happy printing!

Pros and Cons

By Jean-Louis Gassée

This is not about making a m?salliance of respected professionals and confidence men in the same column. No, today's topic is an attempt to balance two opposing views of an issue. That is, the good and bad sides of a hypothetical venture fund whose sole purpose would be investing in Be developers.

Actually, this isn't so hypothetical. This week's choice of topic was triggered by reactions to a mention on our site of Marco Bernasconi's BeFund:

Marco, a long-time friend of Be, is based in Switzerland, and he *is* the fund.

We're all grateful for Marco's role as an "angel" to Be developers and, as the celestial label implies, this is not a classical Silicon Valley venture fund. What many correspondents have asked, however, is whether there should be a standard venture fund for BeOS-centric companies. I'll try to reproduce their arguments for and against, taking full responsibility for whatever distortions I might introduce in the process.

On the pro side, several readers point to the keiretsu approach adopted by Kleiner Perkins in convincing other investors to join them in investments supporting the Go/Eo platform. More recently, Kleiner Perkins took a similar course in leading the creation of a Java Fund.

As is now well understood, the reasoning behind this kind of venture investment is that a new platform needs a critical mass of symbionts such as software or hardware developers; the platform support fund provides capital to these helper companies.

If the platform "ignites," all investors, whether in the platform company or in third-party companies, are in a good position to profit handsomely for being in before success became retroactively obvious. If critical mass isn't achieved, well, they tried, as manly venture investors are supposed to do.

Critics of the idea call it "dirigiste" (that is, interventionist), an affront to the way things are done in free-market heaven. Essentially, they say, if a start-up offers a money-making opportunity, it will get financed. There is so much capital available right now that any good team with a good business plan will attract funding.

In other words, if the free market doesn't fund BeOS developers, listen to what investors are saying: the business plan won't work. Perhaps it's the team, or the product; more likely there isn't enough confidence that the platform will reach critical mass and reward investors accordingly.

Furthermore, opponents of the idea point out, the Go/Eo keiretsu didn't go anywhere, and there is doubt that Java will ever ascend to the Windows-killer platform status originally ordained for it. In other words, the dirigiste idea doesn't work, critics say, looking at my passport.

Others take a different perspective. They point out that most large companies now deploy a strategic investment fund of one kind or another. These days, one of the most visible examples is Intel, but from Adobe and AT&T to 3Com and Cisco, "everybody does it." That does not automatically make it a good idea, but one is tempted to assume these companies have a different conduit for their philanthropic activities.

In other words, all these CEOs, and their boards of directors, believe in the divine intervention of the free market for most things, but they are collectively willing to put billions in play when it comes to creating critical mass, or a self-fulfilling prophecy, rather than letting nature take its course. If memory serves, in the early eighties, a rich Apple took a 20% equity position in the new and, at the time, unproven Adobe. That investment helped the rise of the Macintosh platform and Apple made a killing of Microsoft proportions on its Adobe stake.

In fairness, the examples go back and forth, and for every success story one may find failures of Momenta proportions. There is no absolute truth in this matter of platform-support investments. There is only risk taking, and the small matter of execution.

BeDevTalk Summary

BeDevTalk is an unmonitored discussion group in which technical information is shared by Be developers and interested parties. In this column, we summarize some of the active threads, listed by their subject lines as they appear, verbatim, in the mail.

To subscribe to BeDevTalk, visit the mailing list page on our web site:


Subject: Alternative access to the BeOS API

Is it possible to produce a Be app by cross-compiling on another platform? The libraries are, as Ernest S. Tomlinson pointed out, "Portable Executable binaries", so some clever compiler direction should do the trick. Right?

Fred Fish pointed to a tract in the Microsoft Developer Network Library called "Learn System-Level Win32 Coding Techniques by Writing an API Spy program" that shows you "how to make all client-sharedlib calls go through some private code you supply." Thomas Hudson nominated Chris Herborth's port of SWIG

which "analyzes C++ code and generates an interface to various scripting languages such as TCL/Tk, Python, Perl..."

There was some shouting from the "feel the pain" crowd (REAL BeOS developers use MW/BeIDE). And some listeners took the opportunity to re-open the binary format debate (ELF vs PEF/PE).

Subject: BHandler destructors not called

Should a BHandler be destroyed when its BLooper is destroyed? Jesse Hall sees the looper/handler relationship as similar to that of window/view. And just as dying windows destroy their views, loopers should clean up after themselves.

But maybe not: Matt Brubeck thinks unhitching the fate of a handler from its (current) looper makes message redirection easier, particularly when the looper is a window.

THE BE LINE: To paraphrase Peter Potrebic, the fact that a destructed looper doesn't destroy its attached handlers is not a bug, and won't change. However, the looper should (but doesn't) remove its handlers while its being destroyed. This is a bug and will be fixed.

Subject: buggy rint()

Tinic Uro submitted a mathlib mystery spot:


..produces '8' and '10'. Why?

Jens Kilian offered an explanation:

This is the American way to round a number—if it's exactly halfway between integers, it is rounded to the EVEN one. The German way, which I and (presumably) you were taught, is to simply take ceil(x + 0.5), always rounding UP when halfway between numbers.

International relations at stake, a number of listeners wrote in to suggest using floor() and ceil() (or, for the latter, floor(x+.5)) instead of rint().

THE BE LINE: (From Mani Varadarajan)

“[rint()'s behavior] is correct. To prevent skewing rounding errors only one way, it is a well-established rule that one should always round to the even number if a number is exactly between an even and odd value (pardon the lack of precise mathematical terminology).

If the rule were to round always up, the error would be skewed in one direction. This evens out the error.”

Subject: Java tools

What's Be's Java policy? It was announced that Be had been dealt into Sun's floating crap game, but have they ante'd up? Is there a JVM on the horizon?

THE BE LINE: Be is NOT an official Java licensee. We'd love to see a JVM running on the BeOS, but we have no plans to work on one ourselves.

Subject: Interface paradigm questions

The windows for all instances of a multi-launch app are listed together in the Deskbar's app window list. How do you tell which windows belong to which instance of the app? Suggestions:

  • Map your windows to logical units. For Felix (for example), this means each window would represent a distinct server, and tab views within the window would serve the various channels on that server.

  • Go "docu-centric" and teach the windows to identify themselves. In the context of the previously proposed Felix example, a window would name the server & channel to which it's connected. You don't really care which instance of the app runs a particular window, all you care about is the window's target/contents.

  • Make your app single launch, multi-window. Why relaunch the app just to open another channel? If the app is well-written, you should ALWAYS be able to get another window.

Fine suggestions all, each with their own set of advantages and blemishes. And of the latter for the latter, comes this from Matt Brubeck:

I have NetPositive running in workspace three. I want to open a NetPositive window in workspace one. Ideally, I should be able to go to workspace one, launch NetPositive, and have it open a new window. Currently, I have to go to workspace three, tell NetPositive to open a new window, use Workspaces to drag the window to workspace one, then go to workspace one. Bleah.

Subject: printing long longs


How do you printf() a 64-bit integer?


printf("%Ld", n)

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.