Requirements and considerations for UI Toolkit programs
This topic includes the following sections:
- tools.def
- Reserved global data section and common field names
- Channel usage
- Event-driven programming
- Error processing
- Bounds checking
- Callbacks
- Resizing the application
- Monitor scaling and resolution on Windows
When writing your program, remember to do the following:
- Include the tools.def file.
- Avoid using global data section names reserved by UI Toolkit.
- Use variables for channels.
We also recommend that you use event-style programming when using UI Toolkit. This topic discuss these and other issues you should keep in mind when creating UI Toolkit applications.
tools.def
To use UI Toolkit, your program must include tools.def, which contains definitions of keywords required by Toolkit, as well as external function declarations and global status fields. Use the .INCLUDE statement to include this file in your program. Tools.def is in the directory for your UI Toolkit distribution, so you can use the WND environment variable:
.include "WND:tools.def"
If you want to include only part of tools.def in your application, define D_DEFINES_ONLY or D_NO_GLOBAL_DATA (i.e., use one of these in a .DEFINE statement):
- Defining D_DEFINES_ONLY before including tools.def will include only the Toolkit keyword definitions from tools.def (not function declarations or global status fields).
- Defining D_NO_GLOBAL_DATA will include Toolkit definitions and the function declarations, but not the global status fields.
For example, you could define D_DEFINES_ONLY and include tools.def before the first routine in the file, and then you would need to include tools.def only in routines that need access to the global data or the function declarations.
D_NO_GLOBAL_DATA can be especially helpful when modularizing your code to be used with xfServerPlus. For example, the following code suppresses the inclusion of global data.
.define D_NO_GLOBAL_DATA .include "WND:tools.def"
See the comments in tools.def form more information on this file, and see Customizing configuration fields for information on customizing UI Toolkit with tools.def.
Reserved global data section and common field names
All Toolkit global data section names begin with “DTK_”. UI Toolkit also uses names that begin with “GLST_” for common field names. Therefore, to avoid potential conflicts, avoid using names beginning with “DTK_” or “GLST_” for common fields or global data sections.
Channel usage
The UI Toolkit subroutines use variables for all channels. In the past, most programs “hard-coded” their channel numbers by using decimal literals (1, 15, 32, and so forth). However, hard-coding can get you into trouble—for example, you might inadvertently use a channel that is already in use. Enabling the development environment to manage the I/O channels eliminates this problem, since Toolkit assigns the channels and maintains a database so channels don’t get re-used inappropriately.
UI Toolkit uses three types of channels: system channels, global channels, and local channels.
A system channel is reserved for use by the system. That is, you may not manipulate it with U_OPEN, U_CLOSE, U_GBLCHN, and so forth. The terminal channel opened by U_START is a system channel, and so are all channels that have been excluded from Toolkit use, such as those before or after the first_channel through last_channel range passed to U_START. These channels may be accessed through non-Toolkit calls, but we do not recommend it.
UI Toolkit uses the default terminal channel, which is opened automatically when the program starts. When an OPEN(chn, i, "TT:") or OPEN(chn, o, "TT:") is performed, that channel number is mapped to the default terminal channel. When the channel is closed, the mapping is removed. The default terminal channel is closed only when the program exits.
On OpenVMS, the default terminal channel opens SYS$INPUT and SYS$OUTPUT for input and output. When running a Toolkit program from a command file, if you want to get input from the terminal device instead of the command file, add the line $ define/process sys$input sys$command before the command line where you run your Toolkit program. |
A global channel is a channel that you can open or close. You can either open it as a global channel or promote it to global later. You cannot automatically close a global channel with an E_EXIT from the environment in which it was opened; however, you can close it using a U_CLOSE at any time.
You can also open a local channel, but it will be closed automatically (with purge) when the environment in which it was opened is exited. Unlike local windows, a local channel may also be closed using U_CLOSE at any environment level.
For information about opening and closing channels, see U_OPEN and U_CLOSE.
Event-driven programming
Programming with UI Toolkit is most effective if you use event-driven (or event-style) programming. Toolkit uses black-box processing, and event-driven programming takes full advantage of this feature. For more information, see Black-box processing.
In event-driven programming, control of your program is turned over to UI Toolkit by calling a Toolkit routine. When the Toolkit routine completes its processing, it returns control to your program. Your program tests an exit flag for the “event” that caused the routine to exit. Based on the result of this test, your program performs the appropriate function.
UI Toolkit is designed so this event-driven technique works best in a loop (REPEAT, DO UNTIL, and so forth). We call this the “Toolkit event loop.” It works for any of the UI Toolkit functions: input processing, menu processing, text editing or viewing, or list processing.
At the top of your loop, you call a Toolkit routine. For example, you may call a routine to do input processing. UI Toolkit will start input processing, and any input from the user will be processed by Toolkit.
When Toolkit determines that the user has input something that meets its criteria for termination of input processing, a flag (or flags) will be set, the input routine will exit, and control will be returned to your program.
Immediately following the call to the input processing routine, your program will perform a test, or series of tests, to determine the reason input processing exited. Using the test results, you can have your program call another routine, look up a record in a file, validate data, exit the input processing loop, or anything else that can be done with Synergy DBL.
When your program has done what it needs to do, it returns to the top of the loop and calls the input processing routine to get more input.
The complete steps for using event-driven programming are as follows:
1. | Remove any unnecessary menu columns from the menu bar. |
2. | Place necessary menu columns on the menu bar. |
3. | Set up the following loop: |
- Get user input with direct menu bar selection, text windows, input windows, selection windows, or lists.
- Find out what event occurred. Was a menu entry selected? Is the user done with a text or input window? Do you need to process an input window’s break field? Did the user make a selection?
- Process the event.
- Unless the event is one that terminates processing, return to the first bulleted item under step 3.
Throughout this manual, you will be exposed to the UI Toolkit event loop. We encourage you to study this sample code, and better still, try writing some small programs using this technique. Once you become comfortable with this programming style, you will be well on your way to becoming a productive Toolkit programmer.
Error processing
Synergy DBL runtime errors (with the exception of most window errors) can be trapped with calls to the FATAL subroutine, with ONERROR statements, and with TRY-CATCH blocks.
When a Toolkit error or a window error is generated, the error is automatically sent to U_ABORT, unless the error is returned in an argument for a Toolkit routine, such as the error argument for L_CREATE. (If this happens, the error number is returned in the argument, and you can use %U_GETWNDERR to get the error text.) If an error is sent to U_ABORT, it will either perform a STOP and display a fatal error message (the default), or it will throw an error that can be trapped by an ONERROR statement. For more information, see U_ABORT and %U_GETWNDERR.
Bounds checking
Bounds checking detects memory access errors (buffer overruns), where a reference exceeds the calling routine’s data space, including such failures as data being overwritten and segmentation violations. There are several ways to use bounds checking with Toolkit applications:
- Set the g_dtkbounds global variable or the DTK_BOUNDS environment variable to 1 or 2. (DTK_BOUNDS determines the initial setting of g_dtkbounds.) With these settings, bounds checking is limited to a few input and list routines. Although bounds checking is built into Toolkit libraries on Windows, as discussed below, g_dtkbounds/DTK_BOUNDS has checks that aren’t available with these other bounds checking options. For example, it tells you explicitly when a data area passed to a routine is too small (rather than issuing a generic range error), and on Windows it checks to make sure W_PROC(WP_RESIZE) is not used to resize one of the Toolkit's reserved windows (header, footer, or information line) or an input window associated with a list. For more information, see DTK_BOUNDS and g_dtkbounds.
Note that if g_dtkbounds is set to 2, you can use DTK_BOUNDS_LOG to log nonfatal errors to a file.
- Use the -qstrict or -qcheck Synergy compiler option when you compile your application. These options, which are available in traditional Synergy on Windows and on UNIX and OpenVMS, provide different levels of bounds checking for all routines in your application. See Compiler options.
In traditional Synergy on Windows, the default Toolkit library (tklib.elb) is built with one of the Synergy compiler options for bounds checking (-qstrict) by default. If you want more thorough checking, you can use tklib_qcheck.elb, which is a version of the Toolkit library built with the -qcheck Synergy compiler option. To use this library, link against it (WND:tklib_qcheck.elb) and build your entire application using -qcheck. Note, however, that -qcheck is not generally recommended for production builds.
In Synergy .NET, all applications use bounds checking that is automatically provided by the Synergy .NET compiler. The -qstrict and -qcheck compiler options don’t apply to Synergy .NET, but you can use g_dtkbounds/DTK_BOUNDS and DTK_BOUNDS_LOG. Compiler and runtime checks will generally preclude g_dtkbounds/DTK_BOUNDS checking, but in some cases this checking may detect additional issues.
Callbacks
A callback is a call to a Toolkit program from a routine that is not part of the Toolkit program—i.e., a callback routine. On UNIX and OpenVMS, all callbacks are synchronous. On Windows, a callback can be synchronous (e.g., a call to EUTILS_METHOD) or asynchronous (e.g., a call to click method). Most are asynchronous. Note the following:
- In method subroutines invoked by asynchronous callbacks, code should be kept to a minimum, and operations that could conflict with concurrent processing should be avoided. If you want to invoke a menu entry from a method subroutine, we recommend using the D_SIGNAL operation for %M_SIGNAL.
- Windows prevents exceptions from being thrown across the kernel (user32.dll) boundary. This means that performing a STOP chain in an asynchronous callback or throwing an exception that could be caught in a routine on the other side of an asynchronous callback will cause random hangs and crashes. The Synergy runtime generates an $ERR_NOCHAIN error if a callback attempts to perform a STOP chain, but it does not prevent a callback from throwing an exception that could be caught in a routine on the other side.
Resizing the application
You can resize a UI Toolkit application by using U_RESIZE to specify the number of rows and columns for the application.
With traditional Synergy on Windows, you can also scale an application. That is, you can increase or decrease the size of the display screen, the windows for the application, and the text in those windows.
- To set the initial scaling percentage for an application, set the APP_SCALE environment variable. The default is 100 (no scaling).
- To scale an application programmatically, use the D_SETSCALE subfunction for %U_WNDFONT.
- To scale the application when a user resizes, maximizes, or restores the application, set the SYN_RESIZE_SCALE environment variable.
You can also instruct Toolkit to perform special processing when an application is resized with %EAPPSTATE_METHOD.
Monitor scaling and resolution on Windows
Although we improved support for high-DPI monitors with Synergy/DE version 11, support is limited due to Windows limitations. Note the following:
- Scaling is supported only when set with the primary display scaling setting for Windows (Display > Scale and layout > Change the size of text, apps, and other items) for the monitor that Windows uses as the primary monitor.
- We recommend using the same scaling for all monitors. When using Remote Desktop Connection, use the same scaling for remote machines.
- Display issues may occur for a machine with multiple monitors if they have different DPI specifications. Results will vary depending on which monitor Windows uses as the primary.
- Scaling is not supported for toolbar text and graphics.
- You can use the D_SCALE subfunction for %U_WINMETRICS to programmatically retrieve the Windows scaling factor that is in effect on the system.