Copyright (c) Hyperion Entertainment and contributors.

Screen Programming

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Author

Daniel Jedlicka
Copyright (c) 2013 Daniel Jedlicka
Used by Permission

Introduction

Under AmigaOS, a program window is always open on a screen. The operating system’s graphic environment can use multiple screens at the same time, providing a number of “virtual desktops” for you to work on. Screens may vary in size (resolution), colour depth, and they can also be mouse-dragged to reveal other screens hiding behind. Amiga programs can use their own, private (called “custom”) screens; they can open a “public” screen to share display with other programs; or they can open no screen at all and instead use another program’s public screen as visitors. Quite naturally, the most popular and most visited public screen is the Workbench.

Amigans have always been very proud of their screen system. It’s a well-established concept that hasn’t changed much throughout the years, unlike other parts of Intuition. Yet from a programmer’s point of view, screens have been redefined in AmigaOS Release 4. Well, not the screens per se – it’s the philosophy of their use that has changed. The following article helps understand the current mindset and explains certain new implications for screen programming and usage.

Past to Present

In the past, Amiga programs made a heavy use of custom screens. As the resolution of the computer display was low – the typical screen resolution of the time was mere 640x256 pixels – one could quickly lose overview when having too many programs on the Workbench. It was, therefore, normal that individual programs opened their own screens, through which the user switched as needed. To that end software developers often provided screen configuration as part of their programs’ settings.

But now that AmigaOS-compatible computers use high-resolution displays with 16- or 32-bit depth, custom screens have become less useful than before. Why is that? Well, there are now a number of convincing reasons to open programs on the Workbench by default (especially if their GUI consists of a single window). First of all, a large Workbench can comfortably accommodate most programs you typically run during your computing session. Plus, the race for space on the Workbench has become less of an issue with the emergence of modern applications that are able to collapse (“iconify”) their windows to occupy next to no space on the screen. Moreover, running programs on the Workbench brings the benefit of accessibility: you do not need to swap screens to access other programs, disks or the handy features of AmigaOS’ docking utility, AmiDock. Last but not least, using multiple large screens can really be taxing resource-wise. On some lower-end systems you may quickly run out of graphic memory and bring the OS to a crawl just because a couple of programs decided to open their own screen. Sharing a common public screen – such as the Workbench – is more economical.

Screens in AmigaOS 4.x

The decline in the importance of custom screens (brought about by hardware innovations) has further been accelerated by certain new AmigaOS features that, instead, encourage the use of public screens.

First of all, program windows now allow for greater mobility then before. Using a new feature in Window Class (introduced in AmigaOS 4.1 Update 3), BOOPSI-based programs can “jump” between screens freely during their runtime. This can greatly increase the usability of certain types of program (such as a calculator, a notepad or a dictionary) because they can be pushed to where they are currently needed, without having to reconfigure and restart the program. However, screen jumping only works with public screens because custom screens cannot take visitor windows.

And the importance of public screens in the OS was further pronounced by the introduction of user-defined public screens.

User-defined Public Screens

AmigaOS 4.x received a new Preferences editor, Screens, which unfortunately seems to have been escaping the users' attention. The editor allows users to pre-define their own set of named public screens to utilize throughout the system. They exist as screen definitions known to Intuition, and are only opened when a program requires them.

A screen pre-defined by the user can be made an automatic screen by ticking the “Open/close automatically” option in the Screens preferences editor. For such screens, Intuition handles the opening and closing chores automatically: it is not necessary to call OpenScreenTags() and CloseScreen(). All that is required is that the programmer specifies the screen name when defining the application's main window, via the WA_PubScreenName tag:

...
WA_PubScreenName, “MyPublicScreen”,
...

What happens now is that just before opening the program window (whether it's a standard or a BOOPSI window), Intuition will scan the list of user-defined public screens. If it finds a screen that 1) is called “MyPublicScreen” and 2) is defined as automatic, it will open (and lock) the public screen for you and then open the window on it. If the named screen is not found in the list, or it hasn't been defined as automatic, the window will open on the default public screen (which is normally the Workbench).

Alternatively, you can use the LockPubScreen() function, obtain the screen pointer and pass it to the window via the WA_PubScreen tag (see below). Which means a bit more work and code but it may give you more control over things, should you require it. This will produce the same result as the line of code above (i.e. the window will open on the specified named public screen), the only little difference being that you'll already get the screen opened at LockPubScreen() time:

struct Screen *myPublicScreen = NULL;
 
if ( (myPublicScreen = IIntuition->LockPubScreen("MyPublicScreen")) )
 {
 
  /*
     The automatic screen should now be opened
     and we have its pointer.
 
     So hurry up with opening your window!
     In its definition tag list, use:
 
     ...
     WA_PubScreen, myPublicScreen,
     ...
 
     and then open it.
 
   */
 
 }

Just make sure not to mix up WA_PubScreen (takes a screen pointer) and WA_PubScreenName (takes a STRPTR)!

Any function call or object method that closes the last program window – CloseWindow(), DisposeObject(), WM_CLOSE or WM_ICONIFY – will cause Intuition to also close (and unlock) the screen automatically.

Screens pre-defined without ticking the “Open/close automatically” option must be opened and closed manually in program code. Intuition reads and uses their parameters but administers no kind of automation for them.

Programming Implications

Use automatic public screens if you can

As it was mentioned above, screens can exist as virtual entities defined externally in the Screens preferences editor. Their creation and disposal is automatically handled by Intuition so no GUI code is required whatsoever. Of course you can still use OpenScreenTags() but why bother and do things the complicated way? Opening an automatic public screen by simply using its name is so much easier! (However, see below for Recommended practice.)

Screen configuration GUI

This also means that the screen configuration GUI, as we know it from older Amiga programs, will change. Options to open on a custom screen will be gone because custom screens are not practical anymore (see above). Public screen settings will shrink to a single string gadget for the screen name. Screens will be defined externally according to individual user needs. The screenmode-selection gadget and requester will be used rarely in program GUIs.

Iconification caveats

Older documentation mentioned that a screen can never get closed while there is a program window opened on it: the window works as a screen lock. Although this is perfectly true, some programmers misinterpreted it as “the screen pointer will be valid until your program exits”. Such an assumption no longer works for programs that can iconify their windows – which effectively means most programs of today.

It is not, and never has been, guaranteed that when the user decides to uniconify your program, its original public screen will still be available! Therefore, it is unwise to hold any pointers related to the original screen and/or its properties, such as VisualInfo, rastport, AmigaGuide context etc. If you ever need to keep them, NULL them all at iconify time – you'll obtain them again later. Before the uniconification takes place, first ensure that there is a place for the program to return: LockPubScreen() is a good thing to do. If your program normally opens on a named public screen, provide a Workbench fallback should the lock fail:

if ( !(pubScreen = IIntuition->LockPubScreen("AnyName")) ) pubScreen = IIntuition->LockPubScreen(NULL);
if (pubScreen)
 {
    /* you can safely reopen your window now */
 }

Once you have the new screen pointer, get all other screen-related pointers if your program needs them. If menus are attached using GadTools Library functions, make sure to obtain a new VisualInfo for the screen you'll reopen on, and relayout the menu strip: otherwise the program will crash if it returns to a different screen. When the window is back on, call UnlockPubScreen() and drop the screen lock. Holding locks throughout program runtime might jeopardise the use of automatic public screens.

Screen jumping caveats

To use the screen-jumping feature (see 3.2 above) successfully, you must understand how this feature works and what Window Class does internally. It performs the necessary housekeeping associated with screen change but it doesn't do everything for you. When the user decides to move the program to another screen and selects the screen name from the pop-up menu, WindowClass updates the program window's WA_PubScreen to point to the new screen, and then sends a WMHI_JUMPSCREEN message. It is fully up to the programmer to react upon this message and reopen the window on the new screen, obtaining or updating all relevant pointers in the process.

Be careful if your program uses sub-windows: whether you create them on-the-fly or at program startup, before opening you must always provide them with a WA_PubScreen tag containing the current screen pointer, otherwise the sub-window could end up on a different screen than the main program window. When travelling between places, do not leave your children behind.

Similarly, if your program uses the AmigaGuide Library to provide online help, remember that when the help system is set up, you receive a pointer to something called the “AmigaGuide context”. The context serves as a handle to your AmigaGuide file, and by design is tied to a particular screen on which the file will open when called for. By now it should be quite clear that if the program is designed to allow jumping between screens, the context for the help system must be re-established each time the screen is changed - otherwise the help file will open behind the current screen, which is not what the user expects.

Screen properties

If you need to know about a particular screen's properties (such as public screen name, font pointer etc.), use AmigaOS 4's new function GetScreenAttr() – or GetScreenAttrs() if you want to query about several attributes in one go. Never peek in IntuitionBase or any other system structures.

However, refer to the respective autodocs and study the functions' parameters first! GetScreenAttr() and GetScreenAttrs() are not BOOPSI functions because screens cannot be created as objects. Therefore, although the names seem comfortingly similar to BOOPSI's GetAttr() and GetAttrs(), the obtained attribute values are stored rather differently.

Recommended Practice

All of these things boil down to a couple of recommendations or guidelines to follow in your GUI programming under AmigaOS Release 4:

  • If your program uses a simple GUI, always open on the Workbench by default.
  • If you want to give users the option to open on a separate screen, encourage them to define an automatic screen themselves using AmigaOS's Screens editor. This way you'll 1) ensure that the program's screen will act as a public screen, and 2) go for the easiest implementation because your screen settings will then boil down to a single string gadget for the public screen name. (Alternatively, if your program has no Settings section in the GUI and uses Application Library’s XML-based preferences format, provide a key named “Public Screen” (or similar) for the users to enter the screen name. They can easily edit the prefs file with a text editor or the dedicated PrefsObjectsEditor located in the system's Utilities drawer.)
  • If your program must use a dedicated screen by default (typically: programs with complex interfaces that use many sub-windows), open the screen via OpenScreenTags(). In your program’s Settings, provide a standard screenmode selector and a checkbox option letting the user to clone the Workbench screen parameters (you’ll do this by passing “SA_LikeWorkbench, TRUE” in the OpenScreenTags() call). Make Clone Workbench the default setting.
  • Remember that automatic public screens created through the Screens prefs editor are user-defined. As such, the program(mer) can never quite rely on their parameters. For example, if you write a program that runs on an automatic screen called "MyProgScreen", this screen will likely be defined differently on individual users' systems. So if your program requires a screen of a certain depth, size etc. to function properly, you should open the screen yourself and not rely on the Screens editor.
  • Do not use custom screens unless you absolutely have to. Expect that the user will want to open various programs on your screen.
  • Do not keep public screens locked throughout program runtime. Call UnlockPubScreen() when your program window (re)opens.
  • Be smart and careful in your code that handles window iconification and uniconification. Never assume that your program’s window will reopen on the same screen. Do not keep any global pointers or locks that refer to the original screen or its properties.
  • Now that windows can jump freely to different screens, caching screen pointers does not make any sense. It never did, actually. Always query the window object using GetAttr() when you need the current screen pointer.
  • Use Intuition's GetScreenAttr() or GetScreenAttrs() to find out about screen properties.