Photon Prog Guide
Photon Prog Guide
Publishing history
November 1995 First edition
December 1996 Second edition
April 1998 Third edition
July 2004 Fourth edition
1 Introduction 1
Overview of the Photon architecture 3
Photon Application Builder (PhAB) 6
Widget concepts 8
Widget life cycle 13
Widget geometry 14
Programming paradigm 17
Text-mode application 18
Non-PhAB application 19
PhAB application 20
Photon libraries 21
Contents iii
API categories and libraries 21
Versions and platforms 23
Building applications with PhAB—an overview 24
Step 1: Create modules 24
Step 2: Add widgets 25
Step 3: Attach callbacks 26
Step 4: Generate code 27
Step 5: Run your application 27
Step 6: Repeat any previous step 28
Writing applications without PhAB 28
2 Tutorials 29
Before you start... 31
Creating a Photon project and starting PhAB 31
PhAB’s Interface 32
Tutorial 1 — Hello, world 34
Creating the application 34
Generating code 36
Want more info? 38
Tutorial 2 — editing resources 38
Adding a button widget 39
Changing the bevel width 39
Changing the font 40
Changing the text alignment 42
Setting flags 43
Changing the fill color 44
Editing a pixmap 45
Editing multiline text 46
Editing a list of text items 48
Creating a template 50
Want more info? 53
Tutorial 3 — creating menus and menubars 54
About link callbacks 54
iv Contents
About instance names 55
Creating a menubar 55
Creating the File menu module 56
Adding menu items 57
Creating the Help menu module 59
Attaching link callbacks 59
Setting up the code 62
Want more info? 63
Tutorial 4 — creating dialogs 63
About dialogs 64
More on instance names 64
Attaching a dialog module 65
Adding widgets to the dialog 66
Adding a callback to the Done button 69
Modifying a generated code function 70
Compiling and Running 71
Want more info? 72
Tutorial 5 — creating windows 72
Creating a window 73
Attaching callbacks 73
Adding widgets 74
Generating and modifying the code 77
Compiling and running 81
Want more info? 81
3 PhAB’s Environment 83
Menus 85
File menu 85
Edit menu 86
Project menu 88
Build menu 89
Widget menu 91
View menu 92
Contents v
Window menu 93
Help menu 94
Toolbars 95
Control panels 98
Widget palette 99
Modes (create vs select) 101
Resources panel 102
Callbacks panel 104
Module Tree panel 106
Module Links panel 108
Browse Files panel 109
Search dialog 111
Customizing your PhAB environment 112
General preferences 113
Color preferences 115
Dragging preferences 115
Grid preferences 116
vi Contents
Importing graphics images 141
Exporting files 141
Contents vii
Displaying a picture 164
Using pictures as widget databases 165
Resizing a picture module 165
viii Contents
Clipboard 189
Cutting and copying 189
Pasting 191
Duplicating widgets and containers 192
Deleting widgets or modules 193
Matching widget resources and callbacks 193
Importing graphic files 195
Changing a widget’s class 196
Templates 196
Creating templates 197
Adding a widget class 199
Editing templates 201
Deleting templates 201
Contents ix
Option list resources 216
Font editor 216
List editor 218
Editing existing list items 220
Deleting list items 220
Number editor 220
Text editors 221
Code editor 224
Layout editors 225
Fill layout info editor 226
Row layout info editor 227
Grid layout info editor: 228
Row layout data editor 229
Grid layout data editor 230
Callbacks 231
Editing callbacks 233
Module callbacks 235
Prerealize setup function 237
Postrealize setup function 237
Setup functions are stored in stub files 238
Code callbacks 238
Callback functions are stored in stub files 239
Hotkey callbacks 239
Hotkeys — the basics 239
Specifying the hotkey label 240
Specifying the callback 241
Processing hotkeys 243
Disabling hotkeys 243
Event handlers — raw and filter callbacks 245
x Contents
Resize policy 251
Absolute positioning 255
Aligning widgets using groups 256
Joining widgets into a group 256
Accessing widgets in a group 257
Aligning widgets horizontally or vertically 257
Aligning widgets in rows and columns 258
Using the Group flags 259
Splitting apart a group 261
Constraint management using anchors 261
Anchor resources 264
Using layouts 266
PtFillLayout 270
PtRowLayout 273
PtGridLayout 278
Using hints 290
Enforcing position or size constraints without anchors or layouts
294
Contents xi
Creating a source module 312
Changing the file display 312
Compiling and linking 313
Specifying additional libraries 313
Running make 314
Customizing the build process 315
Running the application 316
Debugging 317
Managing targets 318
The Build menu 319
Including non-PhAB files in your application 320
Eclipse Project applications 320
Multiplatform applications 320
Single-platform applications 321
Adding libraries 321
Making a DLL out of a PhAB application 322
Compiling and linking 322
Initializing your DLL 322
Unloading your DLL 324
xii Contents
Timers 342
Using PtTimer 343
RtTimer* functions 343
Initializing menus 344
Enabling, disabling, or toggling menu items 344
Changing menu-item text 345
Generating menu items 346
Delaying and forcing updates to the display 352
Globally 352
For a specific container 353
Forcing updates 353
Contents xiii
Callbacks 384
Adding callbacks 384
Callback invocation 386
Removing callbacks 388
Examining callbacks 388
Event handlers 389
Adding event handlers 389
Removing event handlers 391
Event handler invocation 392
Widget styles 393
Photon hook 398
xiv Contents
Manifests 419
Internal-link functions 420
Example — displaying a menu 421
Using widget databases 422
Creating a database 423
Preattaching callbacks 423
Assigning unique instance names 423
Creating a dynamic database 423
Widget-database functions 424
Contents xv
16 Context-Sensitive Help 451
Referring to help topics 453
Universal Resource Locator (URL) 453
Topic path 453
Connecting help to widgets 454
Displaying help in the Helpviewer 454
Displaying help in a balloon 455
Help without the ? icon 455
Accessing help from your code 456
xvi Contents
Work procedures 493
Threads 499
Locking the Photon library 499
Multiple event-processing threads 500
Realtime threads 502
Non-Photon and Photon threads 502
Modal operations and threads 503
Exiting a multithreaded program 505
Threads and work procedures 507
Contents xvii
Creating images 541
Caching images 541
Transparency in images 542
Displaying images 543
Manipulating images 544
Releasing images 545
Animation 546
Creating a series of snapshots 547
Cycling through the snapshots 548
Flickerless animation 550
Direct mode 552
Example 554
Video memory offscreen 555
Offscreen locks 560
Alpha blending support 561
Chroma key support 562
Extended raster operations 563
Video modes 565
Gradients 567
Driver-level gradients 567
Application-level gradients 567
Video overlay 568
Example 569
Layers 572
Surfaces 573
Viewports 574
Layer API 575
Using layers 576
Example 577
20 Fonts 581
Symbol metrics 583
Font function libraries 584
xviii Contents
Font names 588
Querying available fonts 589
FontDetails structure 590
Generating font names 590
Example 591
Writing text in a rectangular area 595
Repairing damage to proportional text 599
21 Printing 605
Print contexts 608
Creating a print context 608
Modifying a print context 608
Starting a print job 609
Printing the desired widgets 612
Printing a new page 612
Printing widgets that scroll 613
Suspending and resuming a print job 615
Ending a print job 615
Freeing the print context 616
Example 616
23 Regions 647
Contents xix
Photon coordinate space 649
Region coordinates 650
Region origins 650
Initial dimensions and location 650
About child regions 653
Regions and event clipping 653
Placement and hierarchy 654
Region hierarchy 654
Parent region 655
Brother regions 656
Default placement 656
Specific placement 659
Using regions 660
Opening a region 660
Placing regions 660
System information 662
24 Events 665
Pointer events 667
Emitting events 670
Targeting specific regions 671
Targeting specific widgets 672
Emitting key events 673
Event coordinates 674
Event handlers — raw and filter callbacks 674
Collecting events 678
Event compression 678
Dragging 679
Initiating dragging 679
Handling drag events 683
xx Contents
Window-rendering flags 690
Window-managed flags 691
Window-notify flags 692
Notification callback 694
Example: verifying window closure 694
Getting and setting the window state 696
Managing multiple windows 699
Window-manager functions 699
Running a standalone application 700
Modal dialogs 700
Contents xxi
Parent/child relationships 727
Photon coordinate space 727
Root region 727
Event types 728
How region owners are notified of events 728
Polling 729
Synchronous notification 729
Asynchronous notification 729
Device region 729
Pointer focus 730
Keyboard focus 730
Drag events 731
Drag-and-drop events 731
Photon drivers 731
Input drivers 731
Output drivers 732
Photon window manager 734
Window-frame regions 734
Focus region 735
Workspace region 735
Backdrop region 735
xxii Contents
Photon compose sequences 756
Contents xxiii
Photon in a single window 797
Exiting PhAB 797
Advanced options 798
PHINDOWSOPTS 798
Transferring PhAB projects 799
Debugger launch line 799
Custom widget development and PhAB 800
Using custom TrueType fonts and PhAB 802
Photon Hook DLLs 802
Glossary 813
Index 835
xxiv Contents
List of Figures
Photon’s event space from the user’s perspective. 4
The Photon widget hierarchy. 11
Life cycle of a widget. 13
Anatomy of a PtBasic widget. 15
Widget position and dimensions. 16
Origin of a widget and the position of its children. 17
Structure of a text-mode application. 18
Structure of a Photon application written without PhAB. 20
Structure of a Photon application written with PhAB. 21
Overview of PhAB’s user interface. 33
PhAB’s menubar. 85
PhAB’s toolbars. 95
The nudge tool’s components. 98
PhAB’s widget palette. 100
The Resources panel. 103
The Callbacks panel. 105
The Module Tree panel. 106
The menu for the Module Tree panel. 107
The Module Links panel. 108
The Browse Files panel. 110
The Search dialog. 111
Setting PhAB preferences. 113
Grid Preferences. 116
Choosing the style of the base window. 122
The Open button on PhAB’s toolbar. 123
Application Selector dialog. 124
to find out how Photon and its widgets have changed in this release.
This table may help you find what you need in this book:
continued. . .
continued. . .
• The Raw Drawing and Animation chapter now describes how you
can use layers.
• The Fonts chapter is updated with information about the new font
library.
Errata
• The libraries in /usr/photon/lib are provided for runtime
compatibility with Photon for QNX Neutrino 6.0 (x86 only). The
current libraries are in /usr/lib. For more information about the
libraries, see “Photon libraries” in the Introduction.
• You can now specify a list of library callback functions when you
start PhAB. For more information, see appbuilder in the QNX
Neutrino Utilities Reference.
Introduction
• The geometry of a widget has changed slightly; it now includes the
widget’s border. For more information, see “Widget geometry.”
PhAB’s Environment
• You no longer need to press Enter after giving an instance name to
a widget.
Geometry Management
• In the current version of the Photon microGUI, widgets are
anchored immediately upon creation. In earlier versions,
anchoring is done when the widgets are realized.
• Timers
Other changes:
• Ordering widgets
Context-Sensitive Help
• The PxHelp* functions are now named PtHelp* and are in the
main Photon library, ph.
Interprocess Communication
New sections:
Other changes:
Pt_CONTINUE
The input handler doesn’t recognize the message. If
there are other input handlers attached to the same
process ID, they’re called. If there are no input
handlers attached specifically to this process ID, or if
all input handlers attached specifically to this process
ID return Pt_CONTINUE, the library looks for input
handlers attached to pid 0. If all the input handlers
return Pt_CONTINUE, the library replies to the
message with an ENOSYS.
Pt_END The message has been recognized and processed and
the input handler needs to be removed from the list.
No other input handlers are called for this message.
Pt_HALT The message has been recognized and processed but
the input handler needs to stay on the list. No other
input handlers are called for this message.
Parallel Operations
New sections:
• Threads
• Direct mode
• Video memory offscreen
• Alpha blending support
• Chroma key support
• Extended raster operations
• Video modes
• Gradients
Other changes:
Fonts
New chapter.
Printing
The entire API has been made simpler. Applications that call the old
routines should still work, but you should reread this chapter.
Events
New sections:
• Pointer events
• Event handlers
Typographical conventions
Throughout this manual, we use certain typographical conventions to
distinguish technical terms. In general, the conventions we use
conform to those found in IEEE POSIX publications. The following
table summarizes our conventions:
Reference Example
Code examples if( stream == NULL )
Command options -lR
Commands make
Environment variables PATH
File and pathnames /dev/null
Function names exit()
continued. . .
Reference Example
Keyboard chords Ctrl-Alt-Delete
Keyboard input something you type
Keyboard keys Enter
Program output login:
Programming constants NULL
Programming data types unsigned short
Programming literals 0xFF, "message string"
Variable names stdin
User-interface components Cancel
We use an arrow (→) in directions for accessing menu items, like this:
Technical support
To obtain technical support for any QNX product, visit the Support +
Services area on our website (www.qnx.com). You’ll find a wide
range of support options, including community forums.
In this chapter. . .
Overview of the Photon architecture 3
Photon Application Builder (PhAB) 6
Widget concepts 8
Programming paradigm 17
Photon libraries 21
Building applications with PhAB—an overview 24
Writing applications without PhAB 28
Chapter 1 • Introduction 1
Overview of the Photon architecture
Chapter 1 • Introduction 3
Overview of the Photon architecture
Event space
Root region
Application region
When you run the application, you interact with it, and it interacts
with other applications and Photon, in many ways: you press keys and
mouse buttons, the application performs graphical operations, and so
on.
These interactions are called events; they travel between regions in the
event space like photons or particles of light. For example:
• When you press a mouse button, the device driver emits an event
and sends it back through the event space (toward the root region).
A region that’s interested in the event can catch it and process it,
activating a push button or other UI element.
4 Chapter 1 • Introduction
Overview of the Photon architecture
Each region can determine which events it’s interested in by setting its
sensitivity and opacity:
Chapter 1 • Introduction 5
Photon Application Builder (PhAB)
6 Chapter 1 • Introduction
Photon Application Builder (PhAB)
Concentrate on functionality
Like other GUI development environments, PhAB lets you attach
code functions to a widget’s callbacks so you can implement your
application’s main functionality. For example, you can attach a code
function to a button so that the function is invoked whenever the user
clicks the button.
In addition, PhAB doesn’t force you to write and attach the code
needed to “glue” the different parts of your interface together. Instead,
you can attach a widget’s callbacks directly to any window, dialog, or
menu. The only code you have to worry about is the code specific to
your application.
Chapter 1 • Introduction 7
Widget concepts
all aspects of your application until it looks and works just the way
you want.
Widget concepts
When creating a new user interface (UI), you’ll find it much simpler
to compose the interface from a set of standard components, such as
sliders, lists, menus, and buttons, than to implement each UI element
8 Chapter 1 • Introduction
Widget concepts
• draw itself
In addition, there are some widgets called containers that hold other
widgets and manage their layout.
A widget also hides the details of how it performs these
responsibilities from the outside world. This principle, known as
information hiding, separates the widget’s internal implementation
from its public interface.
The public interface consists of all the attributes visible to other
objects as well as the operations other objects may perform on the
widget. The attributes in the widget’s public interface are called
resources.
Chapter 1 • Introduction 9
Widget concepts
Not every object is unique. Objects that perform the same function
and provide the same public interface belong to the same class.
Widgets that provide the same UI component belong to the same
widget class. The window’s class methods implement the common
functionality by the class.
Several widget classes may have attributes and operations in common.
In such cases, these widget classes may be categorized as subclasses
of the same superclass or parent class. The attributes and operations
are encapsulated in the superclass; the subclasses inherit them from
the parent class. The subclasses themselves are said to be inherited
from the superclass.
The Photon library allows a widget class to be inherited from only
one widget class. This relationship is known as single inheritance.
The relationships between all of the widget classes can be drawn as a
tree known as the class hierarchy.
10 Chapter 1 • Introduction
Widget concepts
PtBarGraph
PtCalendar PtBkgd
PtClock PtClient PtWebClient
PtColorPanel
PtColorPatch
PtColorSel
PtColorSelGroup
PtColorWell
PtComboBox PtFileSel
PtCompound PtDivider PtGenTree PtRawTree
PtGenList PtList PtTree
PtRawList
PtMenuButton
PtMultiText
PtNumericFloat
PtNumeric
PtNumericInteger
PtDisjoint PtWindow
PtContainer
PtFlash
PtFontSel
PtGroup PtMenu
PtImageArea
PtOSContainer
PtPane
PtPanelGroup
PtPrintSel
PtBasic
PtRegion PtServer
PtScrollArea PtScrollContainer
PtTerminal PtTty
PtToolbar PtMenuBar
PtToolbarGroup
PtProgress
PtGauge PtScrollbar
PtSlider
PtArc
PtBezier
PtEllipse
PtWidget PtGrid
PtGraphic
PtLine
PtPixel
PtPolygon
PtRect
PtOnOffButton
PtButton
PtMenuLabel PtToggleButton
PtLabel
PtTab
PtMeter
PtText
PtMTrend
PtRaw
PtSeparator
PtTrend
PtUpDown
PtTimer
Chapter 1 • Introduction 11
Widget concepts
The Photon widget library acts like a widget factory. It provides a set
of functions that let you to create a new widget of a particular widget
class and then manipulate that widget. Once created, the widget has
all the characteristics of the widget class. Because of inheritance, it
also has all the characteristics of the superclasses of its widget class.
The new widget is an instance of the widget class. Creating a new
widget of a particular class is thus also called instantiating the widget.
This term isn’t entirely accurate, however, because you’re really
instantiating the widget class. This reflects a tendency found
throughout this guide to refer to both widgets and widget classes
simply as “widgets.”
The widget’s resources are used to configure its appearance or
behavior. You can edit resources in PhAB, and after the widget has
been created you can change many of them with a call to
PtSetResource() or PtSetResources(). Resources are used extensively
to control the data displayed by a widget and to customize how it’s
displayed. For example:
How you get and set widget resources in your application code
depends on the type of resource. For more information, see the
Manipulating Resources in Application Code chapter.
12 Chapter 1 • Introduction
Widget concepts
Create
Destroy Realize
Unrealize
Chapter 1 • Introduction 13
Widget concepts
Widget geometry
You can think of a widget as a painting or mounted photograph. The
widget is held by a frame called a border. For a widget, the border is
the set of outlines as well as the beveled edge that may be drawn
around the outside.
The part of a widget that’s used for drawing is called the canvas. For
PtWidget, this is the area inside the widget’s borders. For PtBasic
and its descendants, the canvas is the area inside the widget’s border
and margins. Other widgets, such as PtLabel, define other margins.
The margins form a matt and obscure any part of the canvas extending
14 Chapter 1 • Introduction
Widget concepts
Margin
Border
The canvas and margins are shown in different colors in the above
diagram for the sake of clarity. In an actual widget, they’re the same
color.
For a widget, the border is optional. It’s drawn only if the widget is
highlighted (i.e. has Pt_HIGHLIGHTED set in its Pt_ARG_FLAGS
resource). The border consists of various optional components,
depending on the settings of the widget’s Pt_ARG_BASIC_FLAGS
resource. The components, from the outside in, are:
• a one-pixel outline
• a one-pixel inline.
Chapter 1 • Introduction 15
Widget concepts
Origin of parent
x
Margin
width
y
POS (x, y)
Bevel width
Margin height
DIM
(height)
Margin height
DIM (width)
16 Chapter 1 • Introduction
Programming paradigm
Container widget
POS (x, y)
Origin of container
POS (x, y)
Child widget
Programming paradigm
Let’s compare how you write a text-mode application, a non-PhAB
(Photon) application, and a PhAB application.
Chapter 1 • Introduction 17
Programming paradigm
Text-mode application
When you write a non-Photon (text-mode) application, you generally
concentrate on the main program, from which you do such things as:
• iterate
Signal
handlers
Signals
Message
handler
Messages
Message
handler
functions Console
exit()
18 Chapter 1 • Introduction
Programming paradigm
Non-PhAB application
A Photon application written without PhAB is similar to a text-mode
application, except that you also:
Chapter 1 • Introduction 19
Programming paradigm
Main program
Messages Callbacks
PtMainLoop() - Create, realize,
destroy other
windows
Photon events - Handle interrupts
manually
- Create menus
- Create widgets
- Create dialogs
Graphics
- Create widget
driver
callbacks
- ...
exit()
PhAB application
When you develop a PhAB application, the main program is provided
for you. Instead of worrying about the main program, you:
• set up signal handlers, which process the signals as they arrive and
call signal-processing functions that you write
20 Chapter 1 • Introduction
Photon libraries
Initialization
function
Messages
Message
handler
Photon events functions
Graphics
driver Callbacks
exit()
In addition, you don’t have to size and position widgets from your
code; you do it visually in PhAB. PhAB also looks after instantiating,
realizing, unrealizing, and destroying your widgets. PhAB even
provides a menu module to make creating menus easy. You can see
why we recommend using PhAB!
Photon libraries
API categories and libraries
The Photon application programming interface (API) is arranged into
sets of functions, each distinguished by a two-character prefix:
Chapter 1 • Introduction 21
Photon libraries
22 Chapter 1 • Introduction
Photon libraries
Chapter 1 • Introduction 23
Building applications with PhAB—an overview
CAUTION:
! The libphoton.so.1 library is for applications created with version
1.14 of the Photon microGUI only. Don’t combine this library with
the current libraries or header files, or your application won’t run
properly.
The libraries in /usr/photon/lib are provided for runtime
compatibility with Photon for QNX Neutrino 6.0 (x86 only). The
current libraries are in /usr/lib.
If you need to determine the version number of the libraries, you can
use:
• PhLibVersion() at runtime.
24 Chapter 1 • Introduction
Building applications with PhAB—an overview
You could design a UI with just one module. But for most
applications, you’ll probably use several modules and assign each a
different role. As a rule, each module groups together related
information and lets the user interact with that information in a
specific way. To help you handle the requirements of virtually any
application, PhAB provides several module types:
Chapter 1 • Introduction 25
Building applications with PhAB—an overview
To customize how a widget looks and works, you set its attributes or
resources. PhAB’s Control panels and Resource editors make it easy
to do this. Just click on the resource you want to change, then select
or enter a new value.
You can even customize a widget and then save it as a template to use
to create similar widgets.
For more information, see the Editing Resources and Callbacks in
PhAB chapter.
26 Chapter 1 • Introduction
Building applications with PhAB—an overview
Chapter 1 • Introduction 27
Writing applications without PhAB
If you’re using PhAB standalone, run the application from the Build
& Run dialog. Using this same dialog, you can even launch your
application under a debugger for seamless debugging.
For more information, see the Generating, Compiling, and Running
Code chapter.
28 Chapter 1 • Introduction
Chapter 2
Tutorials
In this chapter. . .
Before you start... 31
PhAB’s Interface 32
Tutorial 1 — Hello, world 34
Tutorial 2 — editing resources 38
Tutorial 3 — creating menus and menubars 54
Tutorial 4 — creating dialogs 63
Tutorial 5 — creating windows 72
Chapter 2 • Tutorials 29
Before you start...
The best way to get to know PhAB is to use it. This chapter provides
hands-on sessions to give you a jump start on creating applications.
We’ll take a closer look at using PhAB in the chapters that follow.
The first two tutorials cover the basics: creating widgets, changing
how widgets look and behave, generating code, running your
application, and so on.
The remaining tutorials go beyond the basics to show you how to
create working menus, dialogs, and windows. When you’ve
completed these tutorials, you’ll be ready to start building almost any
Photon application.
• New projects — When using the IDE, you create a QNX Photon
Appbuilder project in the IDE, and then use PhAB to create the
user interface. Using standalone PhAB, you create the project
from within PhAB.
• Editing code — The IDE allows you to edit your project’s code,
and take advantage of features like syntax highlighting. When you
use standalone PhAB, you use an external editor, such as vi, to
edit code.
Chapter 2 • Tutorials 31
PhAB’s Interface
appbuilder
PhAB’s Interface
Before you start the tutorials, take a moment to make yourself
familiar with PhAB’s user interface:
32 Chapter 2 • Tutorials
PhAB’s Interface
Chapter 2 • Tutorials 33
Tutorial 1 — Hello, world
The widget palette and control panels are initially in the same
window, but you can drag any of them into a different window. To
switch between panels in a window, click the tab at the top and
choose a panel from the menu.
If you close a control panel, you can redisplay it by selecting the
appropriate item from the View menu.
2 PhAB displays a dialog to let you choose the style for the new
application’s default base window:
34 Chapter 2 • Tutorials
Tutorial 1 — Hello, world
3 Choose a style and click Done; PhAB creates the base window
and displays it.
6 If the widget palette isn’t displayed, click the tab at the top of
the current control panel and choose Widgets from the menu
that appears.
7 Drag the widget palette away from the other control panels by
pointing to the left of its tab, holding down the mouse button,
and pointing to PhAB’s work area.
Chapter 2 • Tutorials 35
Tutorial 1 — Hello, world
12 Change the text to Hello World. As you type, the text in the
widget changes:
Generating code
Now you’re ready to generate, compile, and execute the application.
How you perform this step depends on whether you’re using PhAB
from the IDE or standalone.
36 Chapter 2 • Tutorials
Tutorial 1 — Hello, world
Chapter 2 • Tutorials 37
Tutorial 2 — editing resources
38 Chapter 2 • Tutorials
Tutorial 2 — editing resources
• text fonts
• text strings
• flags
• colors
• pixmaps
You’ll also learn how to create a template so you can create other
instances of an existing widget.
4 Drag any of the button’s resize handles until the button matches
the following picture:
1 Click the Bevel Width resource in the Control Panel. You’ll see
the number editor:
Chapter 2 • Tutorials 39
Tutorial 2 — editing resources
This editor lets you change the value of any numerical widget
resource.
3 To apply the new value and close the editor, press Enter or click
Done.
You can also edit this resource (and most resources) right in the
Resources control panel. Choose whichever method you like.
1 Click the Font resource. You’ll see the font editor, which
displays the button’s current font:
40 Chapter 2 • Tutorials
Tutorial 2 — editing resources
This editor lets you change the text font of any widget that has
text.
2 Click the Font box or the Size box, select a typeface or size
from the displayed list, and click Apply. The button displays
the new font.
3 Click Default. The editor displays the widget’s default font, but
doesn’t apply the font to the widget.
4 If you want to keep the new font that you selected, click Cancel
to ignore the default. If you want to apply the default, click
Done. Either way, the editor closes.
Chapter 2 • Tutorials 41
Tutorial 2 — editing resources
3 Click Done.
42 Chapter 2 • Tutorials
Tutorial 2 — editing resources
You can also set this resource right in the Resources control panel.
Setting flags
Let’s now use the flag/option editor to set one of the widget’s flags:
Chapter 2 • Tutorials 43
Tutorial 2 — editing resources
1 Click the button’s Color: Fill resource. You’ll see the color
editor, which displays the current fill color:
This editor lets you edit any color resource. It provides several
preset base colors, which should work well with all graphic
drivers, and 48 customizable colors for drivers that support 256
or more colors.
2 Click any color in the Base Colors set, then click on Apply. The
button is filled with the color you selected.
3 Select a color from the Custom Colors set. The sliders will
display the color’s Red/Green/Blue (RGB) values. Change
these values till you get a color you want, then apply your
changes.
44 Chapter 2 • Tutorials
Tutorial 2 — editing resources
Don’t delete this widget; we’ll use it to create a template later on, so
that you can create other widgets like it.
Editing a pixmap
Let’s now use the pixmap editor to edit a PtLabel widget. This
editor is called “pixmap” instead of “bitmap” since it lets you edit
many types of image resources besides bitmaps.
A PtLabel widget display text and/or an image.
2 Move the pointer into the main window and click below the
button widget you created. You’ll see a PtLabel widget.
5 Next, bring up the color editor to select a draw color. Just click
the following button:
Chapter 2 • Tutorials 45
Tutorial 2 — editing resources
6 Select a color from the pixmap palette. You’ll see that the draw
color in the pixmap editor changes immediately.
If you click Edit Color, you’ll see the Color Editor, as described
earlier.
2 Move the pointer below the label widget you’ve just created,
and drag until the new PtMultiText widget appears big
enough to hold a few lines of text.
46 Chapter 2 • Tutorials
Tutorial 2 — editing resources
4 Type a few lines of text. To create a new line, press Enter. For
example:
Mary hadEnter
aEnter
little lamb.Enter
5 Click Done. Your text should appear exactly as you typed it. If
it doesn’t, try resizing the widget—the widget might not be
wide enough or tall enough.
Chapter 2 • Tutorials 47
Tutorial 2 — editing resources
You can edit the text right in the control panel, but it displays only the
current line of text.
2 Move the pointer into the application’s base window, and drag
the pointer until the new PtList widget appears big enough to
hold a few lines of text.
48 Chapter 2 • Tutorials
Tutorial 2 — editing resources
4 Click the text box at the bottom of the editor. You’ll see the
text-input cursor.
5 Type some text, then click Add After to place the first item in
the list.
6 Now let’s create the second item. Click in the text box, and type
Ctrl-U to erase the text in the text box, then type some new text.
Click Add After to place this new item after the previous item.
8 Click Apply. The PtList widget should now display the list
you’ve created.
Chapter 2 • Tutorials 49
Tutorial 2 — editing resources
Creating a template
At times, you might want to create many widgets that look and behave
alike. You can do this by creating a widget, editing its resources, and
then copying and pasting it, but this isn’t always very convenient, and
doesn’t copy certain important elements like callbacks.
PhAB makes it simpler by letting you create a template from an
existing widget or widgets. PhAB creates a palette, similar to the
widget palette, for your templates.
Let’s create a template from the button that you created earlier in this
tutorial.
2 Click the Widget menu, and then choose Define Template. The
Define Template dialog appears.
50 Chapter 2 • Tutorials
Tutorial 2 — editing resources
6 You can create an icon for the palette entry for the template. If
you do not create an icon for the template entry, a default icon
is used for it. To create the icon, click the icon Edit button, and
then follow the instructions given earlier for editing pixmaps.
You should make the icon look something like the widget:
Chapter 2 • Tutorials 51
Tutorial 2 — editing resources
Click Done.
You’ve just created a template! Now, let’s see how to use it.
2 Go to the control panels, and click the top tab. The popup menu
now includes My_templates; choose it to display the palette.
52 Chapter 2 • Tutorials
Tutorial 2 — editing resources
If you wish, you can save, generate, make, and run the application.
Whenever you start PhAB, it automatically loads the palette for
My_templates.
Chapter 2 • Tutorials 53
Tutorial 3 — creating menus and menubars
54 Chapter 2 • Tutorials
Tutorial 3 — creating menus and menubars
Creating a menubar
To learn about using link callbacks, let’s create two functioning
menus—File and Help—that you can later incorporate into your own
applications.
In PhAB, menus are built in two pieces:
Using link callbacks, you’ll link the menu modules to the File and
Help buttons in a menubar. You’ll also link a code-type callback to
the Quit menu item in the File menu module. This callback will
enable the Quit item to close the application.
Chapter 2 • Tutorials 55
Tutorial 3 — creating menus and menubars
If you accidentally click the Test button, the window won’t resize or
accept new widgets. If this happens, you just switched into Test
Mode. To go back to Edit Mode, select Project→Edit mode.
By the time you’re finished the following steps, the menubar
will look like this:
56 Chapter 2 • Tutorials
Tutorial 3 — creating menus and menubars
Chapter 2 • Tutorials 57
Tutorial 3 — creating menus and menubars
If you look at the Menu Items list, you’ll see that the <New>
item is selected. This special item lets you add menu items to
the menu.
3 Now give the item an instance name. In the Inst Name field,
type file_new.
4 Click Apply to add the item to the menu. You’ll see the item’s
name in Menu Items list, prefixed by CMD. The CMD means this
is a Command item; that is, an item that invokes a PhAB
callback.
5 Repeat the above steps to create the two menu items labeled
Save and Save As. Give these items the instance names
file_save and file_as.
8 Now let’s add the Quit item. Click the Command button, then
specify Quit as the item text and file_quit as the instance
name.
9 You’re finished with this menu module for now, so click Done.
The module displays the items you just created:
58 Chapter 2 • Tutorials
Tutorial 3 — creating menus and menubars
10 You’ll want to keep this module neatly out of the way while you
work on your next task. So click the module’s minimize button
(the left button at the right side of the title bar), or select the
Work menu button (upper-left corner) and choose Minimize.
Chapter 2 • Tutorials 59
Tutorial 3 — creating menus and menubars
2 To have the File menu module pop up when you press the File
button, you need to attach an Arm callback to the button. By
attaching an Arm callback, you can open the menu using either
click-move-click or press-drag-release.
Click Arm to bring up the callback editor.
60 Chapter 2 • Tutorials
Tutorial 3 — creating menus and menubars
3 The Module Types area of the editor let you choose the type of
module you wish to link to. Because you want to link the File
button to a menu module, click Menu.
4 Click the Name list and type filemenu (or select filemenu
from the list) which is the name you gave your File menu
module. This links the menu button to that module.
You can also select filemenu from a popup list of available
modules. To bring up the list, click the icon to the right of the
Name field.
5 Click Apply to add the link callback, then click Done to close
the callback editor.
6 Repeat the above steps to link the Help menu button to the Help
menu module.
4 Click the icon next to the Callback field to open the callback
editor:
5 When the editor opens, the default callback type is Code. Since
this is the type you want, all you have to do is specify the name
of the function you want to call.
The function should have a meaningful name. So type quit in
the Function field.
Chapter 2 • Tutorials 61
Tutorial 3 — creating menus and menubars
PtExit( EXIT_SUCCESS );
return( Pt_CONTINUE );
}
PtExit() is a function that cleans up the Photon environment and
then exits the application. It’s described in the Photon Library
Reference.
62 Chapter 2 • Tutorials
Tutorial 4 — creating dialogs
If you are using the IDE, you can also edit quit.c, or any other source
code file, from the IDE editor by double-clicking the file in the project
navigator tree.
4 After you’ve edited the code, saved your changes, and closed
the editor, build and run your application.
Chapter 2 • Tutorials 63
Tutorial 4 — creating dialogs
• define a setup function that changes the text of one of the labels to
display a version number when the dialog is realized.
About dialogs
Dialog modules are designed to let you obtain additional information
from the user. Typically, you use this information to carry out a
particular command or task.
Since you don’t usually need to get the same information twice,
dialogs are single-instance modules. That is, you can’t realize the
same dialog more than once at the same time. If you try create a
second instance of a dialog, PhAB simply brings the existing dialog to
the front and gives it focus.
If you need to create a window that supports multiple instances, use a
window module. You’ll learn about window modules in the next
tutorial.
64 Chapter 2 • Tutorials
Tutorial 4 — creating dialogs
2 Open the Help menu module you created (it may still be
iconified).
4 Select the About Demo item, then click the icon next to the
Callback field to open the callback editor:
Chapter 2 • Tutorials 65
Tutorial 4 — creating dialogs
9 Click the Location icon to specify where you want the dialog to
appear when it gets realized. (The Center Screen location is a
good choice.) Click Done.
Your callback information should now look something like this
(depending on the location you chose):
10 Click the Apply in the Actions group to add the link callback.
Since the dialog module you want to link to doesn’t exist yet,
PhAB asks you to choose a style; select Plain and click Done.
You’ll see the new dialog in the work area. You’ll also see the
new callback in the Callbacks list in the callback editor.
11 Click Done to close the callback editor, then click Done again
to close the menu editor.
2 Place two PtLabel widgets in the top half of the dialog, and a
PtButton near the bottom:
66 Chapter 2 • Tutorials
Tutorial 4 — creating dialogs
3 Select the top PtLabel widget and change its label text
resource to About this Demo. Then change its horizontal
alignment to Pt_CENTER.
4 Select the other PtLabel widget and change its label text to a
blank string. Then change its horizontal alignment to
Pt_CENTER.
Later, you’ll fill in the aboutdlg_setup() function so that it
changes the blank text of this label to display a version number.
6 Select the PtButton widget and change its button text resource
to Done. Then change its instance name to about_done.
Chapter 2 • Tutorials 67
Tutorial 4 — creating dialogs
68 Chapter 2 • Tutorials
Tutorial 4 — creating dialogs
3 Select the Done code type, then click Apply. Don’t enter
anything in the Function field.
Selecting the Done code type tells the widget to perform a
“Done” operation when the widget is activated. That is, the
widget calls the function specified in the Function field (if one
is specified) and then closes the dialog module.
4 Close the editor. The callback list now indicates that you’ve
added an Activate callback called Done:
Chapter 2 • Tutorials 69
Tutorial 4 — creating dialogs
return( Pt_CONTINUE );
}
to the following:
int
aboutdlg_setup( PtWidget_t *link_instance,
ApInfo_t *apinfo,
70 Chapter 2 • Tutorials
Tutorial 4 — creating dialogs
PtCallbackInfo_t *cbinfo )
{
return( Pt_CONTINUE );
}
The code is placing the version number (1.00) into the text
string resource for the about_version widget. To do this, the
code calls PtSetResource() to set the resource for the
about_version widget. The code uses the PhAB-generated
manifest ABW_about_version, which provides access to the
widget’s instance pointer.
We can use this manifest safely since we’re dealing with a
dialog module—PhAB ensures that only one instance of the
dialog will exist at any given time.
2 From the running application, open the Help menu and choose
About Demo. The dialog will open, and you’ll see the version
number (1.00) under the label About this Demo. Note that the
dialog appears in the location you specified.
Chapter 2 • Tutorials 71
Tutorial 5 — creating windows
72 Chapter 2 • Tutorials
Tutorial 5 — creating windows
Creating a window
To start, let’s create a window module and attach it to the New menu
item in the File menu in tut4. This window will contain buttons that
change the color of another widget.
In the previous tutorial, you created a dialog module from within the
callback editor. But this time you’ll add the window from the Project
menu. In the future, use whatever method you prefer.
Attaching callbacks
Because a window module supports multiple instances, you have to
create code functions that will be called whenever the window opens
or closes (i.e. whenever the window is created or destroyed). So let’s
first set up a callback to detect when the window closes:
Chapter 2 • Tutorials 73
Tutorial 5 — creating windows
7 Choose the Window module type, then click the arrow next to
the Name field. You’ll see the list of existing window modules.
10 Click Apply, then on Done. Click Done again to close the menu
editor.
Adding widgets
Let’s now add some widgets to the newwin window module. Using
these widgets, you’ll learn how to update information in the current or
other instances of a window module.
74 Chapter 2 • Tutorials
Tutorial 5 — creating windows
Chapter 2 • Tutorials 75
Tutorial 5 — creating windows
76 Chapter 2 • Tutorials
Tutorial 5 — creating windows
int win_ctr = 0;
PtWidget_t *win[5];
int
newwin_setup( PtWidget_t *link_instance,
ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
char buffer[40];
Chapter 2 • Tutorials 77
Tutorial 5 — creating windows
if ( win_ctr == 5 ) {
return( Pt_END );
}
return( Pt_CONTINUE );
78 Chapter 2 • Tutorials
Tutorial 5 — creating windows
int
color_change( PtWidget_t *widget, ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
int i, prev;
PtWidget_t *this_window;
Chapter 2 • Tutorials 79
Tutorial 5 — creating windows
prev = -1;
for ( i = 0; i < win_ctr; i++ ) {
if ( win[i] == this_window ) {
prev = i - 1;
break;
}
}
return( Pt_CONTINUE );
}
int
newwin_close( PtWidget_t *widget, ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
PhWindowEvent_t *we = cbinfo->cbdata;
int i;
80 Chapter 2 • Tutorials
Tutorial 5 — creating windows
return( Pt_CONTINUE );
}
continued. . .
Chapter 2 • Tutorials 81
Tutorial 5 — creating windows
82 Chapter 2 • Tutorials
Chapter 3
PhAB’s Environment
In this chapter. . .
Menus 85
Toolbars 95
Control panels 98
Widget palette 99
Resources panel 102
Callbacks panel 104
Module Tree panel 106
Module Links panel 108
Browse Files panel 109
Search dialog 111
Customizing your PhAB environment 112
Menus
Across the top of PhAB’s workspace you’ll see the following
menubar:
PhAB’s menubar.
File menu
Commands that deal with your application and its files:
Revert Discard any changes and re-load the project from the last
saved version.
Save
Save As Save the current application, under the same or a
different name; see “Saving an application” in the
Working with Applications chapter. The Save command
is also available through PhAB’s toolbars. Save As is
available in Standalone PhAB only* .
This menu also lists the last few applications that you edited.
* When using PhAB in the IDE, projects are managed from the IDE,
so these menu items are disabled. For more information see “Creating
a QNX Photon Appbuilder Project”, in the Developing Photon
Applications chapter of the IDE User’s Guide.
Edit menu
Commands for editing widgets:
Undo
Redo Undo and redo an operation, including:
• creating, deleting, moving, resizing, aligning, and
selecting a widget
• changing the order of widgets
• changing a callback resource — PhAB can’t
undo/redo any changes you make to a callback
function’s code
• pasting
• joining and splitting a group
• editing a resource
• importing images, XBMs, and modules.
• changing a widget’s class, matching widget
resources and callbacks, and transferring widgets
Project menu
Commands for building and configuring a project:
Add Window Open the New Window Style dialog to add a new
window to your project.
Add Dialog Open the New Dialog Style dialog to add a new
dialog to your project.
Edit Mode Switch to edit mode, which means you can add
widgets and modules to your projects, add
callbacks, and so on. When PhAB is launched, it
starts in this mode.
Test Mode Switch to test mode, which means you can test the
appearance of your GUI.
Zoom Mode Allows you to zoom in, zoom out or move around,
by using the mouse and keyboard. After zooming
in or out on the desired area, you must return to
Edit Mode or Test Mode to continue editing or
testing.
Build menu
Commands for compiling and running an application:
Build & Run Standalone PhAB only* . Builds the current project,
and then runs it. If required, this command will
save open files, generate support files, compile, and
link the application. See the Generating,
Compiling, and Running Code chapter.
Build & Debug Standalone PhAB only* . Builds the current project,
then launches it inside the preferred debugger. You
can set breakpoints, step instruction by instruction,
etc. See the Generating, Compiling, and Running
Code chapter.
* When using PhAB in the IDE, projects are built and run from the
IDE, so these menu items are disabled. For more information see
“Building a QNX Photon Appbuilder Project”, in the Developing
Photon Applications chapter of the IDE User’s Guide.
Widget menu
Commands for manipulating widgets:
Group
Ungroup Combine selected widgets into a group, or split up
a selected group; see “Aligning widgets using
groups” in the Geometry Management chapter.
Define Template
A template is a customized widget that you want to
use as the basis for other widgets. This command
opens the Define template dialog, which lets you
create or edit a template; see “Templates” in the
Creating Widgets in PhAB chapter.
View menu
Commands that change the way the modules in the work area are
displayed:
Zoom In
Zoom Out Zoom the display in or out. Use these commands
for doing precise alignments of your widgets. For
example, if you zoom out so that the zoom factor is
less than 100%, your workspace is larger, helping
you work on a 1280x1024 application even if your
display is set to 800x600.
Show Grid Toggles the grid display. The settings for the grid
can be adjusted on the Grid tab of the AppBuilder
Snap to Grid Toggles the snap to grid option. When this option is
on, new or moved widgets are “snapped” to the
grid—that is, they are aligned with the closest
vertical and horizontal grid lines. The grid does not
have to be visible for this option to work.
Show Selection
Toggles the widget selection option. When this
option is on, selected widgets are highlighted with
colored rectangles. When multiple widgets are
selected, the first widget has a different selection
color than subsequently selected widgets. This
makes it easier to align or match widgets, where the
first selected widget is the source. It also makes it
easier to see what’s exactly selected when widgets
overlap.
Window menu
Commands that manipulate PhAB’s windows:
Show Templates
Show Resources
Show Callbacks
Show Module Links
Show Module Tree
Show Project
These commands show or hide control panels for
defined templates, resources, callbacks, module
links, module trees, and project files. If a control
panel is shown, the corresponding menu command
changes to Hide.
Help menu
Get online help information:
Welcome to PhAB
Tutorials
PhAB Concepts
Tools + Techniques
Links to the appropriate section of this
programmer’s guide.
Toolbars
The toolbars give you quick access to frequently used commands
from the menu bar:
PhAB’s toolbars.
Cut
Copy
Paste
Delete and copy widgets to the clipboard, and paste
them from it; see “Clipboard” in the Creating
Widgets in PhAB chapter. These commands are also
available through the Edit menu.
Edit Mode Switch to edit mode, where you can add widgets and
modules to your project, add callbacks, and so on.
This is the default mode when PhAB is launched.
This command corresponds to the Edit Mode
command in the Project menu.
Test Mode Switch to test mode, where you can test the
appearance of your GUI. This command corresponds
to the Test Mode command in the Project menu.
Raise
Lower
To Front
To Back Move the selected widgets forward or backward in,
or to the front or back of the widgets in the
container; see “Ordering widgets” in the Creating
Widgets in PhAB chapter. The To Front and To Back
commands are also available through the Arrange
submenu of the Widget menu.
Group
Ungroup Combine selected widgets into a group, or break up
a selected group; see “Aligning widgets using
groups” in the Geometry Management chapter.
These commands are also available through the
Widget menu.
X
Y
W
H The coordinates and size of the currently selected
widget. To change them, type the new values and
press Enter.
To avoid changing a coordinate or dimension for the
current widget, lock it by clicking on the padlock so
that it closes. You can’t change the field (either by
typing or dragging) until you unlock it, although you
Nudge tool This tool lets you move, expand, or shrink a widget.
Click on the button for the desired mode, and then
click on the frame buttons above:
Frame buttons
You can also use the Ctrl key and the numeric keypad to nudge,
stretch, or shrink a widget. Each key corresponds to one of the nudge
buttons. Pressing Ctrl-5 switches between modes, and Ctrl-↑ works
like the tool’s top frame button.
Control panels
PhAB includes a set of control panels (also referred to as “palette
windows”) that display information about the currently selected
widget or widgets. They’re displayed by default in PhAB, and you
can move them anywhere you like. If you close a control panel, you
can reopen it by choosing the appropriate item from the View menu.
The control panels include:
• Widget palette
• Resources panel
• Callbacks panel
Widget palette
The widget palette lets you add widgets to your application.
The widgets are arranged and color-coded by type. The names are
optional; to hide or display them, right-click on the palette and choose
the appropriate item from the pop-up menu.
To find out what widget a button represents if the widget names aren’t
displayed:
Select mode Lets you select existing widgets and modules in the
work area.
• Click on the background of the PhAB work area. Note that this
might not work if the selected template in the Widget palette
creates a module. In this case, even if you click on the background
of PhAB, the module specified by the template is created at the
pointer position.
Or:
Resources panel
The Resources panel displays a list of resources for the selected
widget or widgets. (If more than one widget is selected, this panel
displays only the resources they have in common.) Here’s an example:
Instance name Lets you enter a unique instance name for the
widget. For more information, see “Instance
names” in the Creating Widgets in PhAB chapter.
You can change the value of a resource right in the control panel, or
you can use the full-featured editor by clicking on the resource name.
For more information, see the Editing Resources and Callbacks in
PhAB chapter.
By default, the Resources and Callbacks control panels display
resource labels descriptively. If you pause the pointer over a resource,
the header manifest is displayed in a popup balloon.
To have the labels displayed as the actual header manifests
(convenient when writing code), open the Preferences dialog and
change the setting in the Resource Names field. To open this dialog,
choose Preferences from the Edit menu.
Now if you pause the pointer over a resource, the popup balloon
displays the descriptive label. You can also copy a resource manifest
or value by right clicking on the left column of the resources or
callback panel. Select Copy resource manifest to ph clipboard to
copy the resource manifest (for example,
Pt_ARG_WINDOW_RENDER_FLAGS). Select Copy resource
value to ph clipboard to copy the actual resource value.
The control panel doesn’t display all the resources for a widget.
PhAB sets Pt_ARG_AREA, Pt_ARG_DIM, Pt_ARG_EXTENT, and
Pt_ARG_POS automatically when you move or resize a widget.
Some other resources are too complex to edit in PhAB.
Callbacks panel
The Callbacks panel displays a list of callback resources for the
selected widget. You can use this panel only when you’ve selected a
single widget. The widget must have a unique instance name. Here’s
an example:
If you close this panel, you can reopen it by choosing Show Callbacks
from the Window menu.
This panel, like the Resources panel, displays the widget class and
instance names, and the next and previous buttons.
The left side of the list indicates the callback type. The right side
displays:
If you close this panel, you can reopen it by choosing Show Module
Tree from the Window menu.
This panel makes it easy to:
If you close this panel, you can reopen it by choosing Module Links
from the View menu.
Use the Filter to select or enter a regular expression to filter the list of
files. For example, *.c displays only files with a .c extension.
Search dialog
The Search dialog lets you search your application for widgets of a
specified type, name, text resource, and so on.
Widget Name In the text field, type the exact name of the widget
or a regular expression. For example, a value of
my_button* matches all the widgets whose
names begin with my_button.
2 Click on the tab for the group of settings you wish to change:
General, Colors, Dragging, or Grid.
General preferences
You can set the following general preferences:
Resource Names
By default, the Resources and Callbacks panels
display resource labels descriptively. This field lets
you display the labels as the actual header manifests,
which you may find helpful when writing code. Note,
however, that manifests are long and take up more
screen space.
If you pause the pointer over a resource, the label not
displayed in the control panel is displayed in a popup
balloon.
Edit Command
Lets you specify an external editor to edit source files.
View Command
Lets you specify the file viewer to use to view source
files.
Print Command
Lets you specify the print command used to print a
selected file (in the Project tab, for example).
Debug Command
Lets you specify the command used to debug your
application.
Automatically save
Whether or not to save the application automatically,
and how often.
Warnings on exiting
Whether or not to warn you when you exit PhAB
without generating or saving your application.
Clipboard Operations
Lets you specify if callbacks should be saved along
with widgets when copying from or cutting to the
clipboard.
Color preferences
You can set the following color preferences:
Resize Handle
Non-Resizable Handle
If you choose a window background that makes it difficult to
see resize handles, use these options to customize the color. (If
you select a widget and the resize handles appear in the
nonresize color, the widget can’t be resized.)
Selection Color
The color for widgets selected after the first widget. Turn this
feature on by selecting View→Show Selection.
Dragging preferences
You can set the following dragging preferences:
Widget
Module Drag widgets and modules as outlines rather than as full
objects.
Grid preferences
You can use a grid to position and size widgets.
Grid Preferences.
You can:
In this chapter. . .
Creating an application 121
Opening an application 123
Saving an application 125
Closing an application 128
Specifying project properties 128
Importing files 139
Exporting files 141
Creating an application
The way you create a new application depends on whether you’re
using PhAB from the IDE or standalone.
Standalone PhAB:
To create a new application, choose New from the File menu or press
Ctrl-N. If you’re already working on an application, PhAB asks if you
want to save any changes to that application before closing it.
You should develop a naming convention for all the widgets, modules,
functions, and so on. This will make managing your application
easier.
Opening an application
The way you open an existing application depends on whether you’re
using PhAB in the IDE or standalone.
Standalone PhAB
To open an existing application, choose Open from the File menu,
press Ctrl-O, or choose Open from PhAB’s toolbar:
Saving an application
You can save your application in several ways, as described in the
sections below.
1 Choose Save As from the File menu. You’ll see the application
selector dialog.
If you type a new directory name, it’s saved. The next time you want
to look in that directory, click the button to the right of the directory
field and select the directory from the list.
Closing an application
When using PhAB from the IDE:
• Press F2.
• a startup window
• a global header
• an initialization function.
• It acts as the default parent window for all other windows and
dialogs.
Window Name
The name of the window module. To select from a list
of existing windows, click the icon next to this field. If
you specify the name of a module that doesn’t exist,
PhAB asks whether it should create that module.
Window Location
Where the window will appear; see “Positioning a
module” in the Working with Modules chapter.
Setup Function
The function that’s called when the window is realized
(optional). To edit the function, click the icon next to
this field.
The buttons below the function name determine whether
the setup function is called before the window is
realized, after the window is realized, or both.
To add a new window to the startup window list, click <NEW>, fill in
the window information, and click Apply.
2 In the Global Header field, type the name of the header file you
plan to use. You don’t have to include the .h extension.
For example, to set up a globals.h header file, you can
simply enter: globals
If you specify the header after some code has been generated, you’ll
have to go back and manually add the header to the stub files that
were previously generated.
Initialization function
Your application can include an initialization function that’s called
before any modules or widgets are created. In it you can initialize
data, open widget databases, set up signal handlers, and so on. To set
up an initialization function:
-s server_name
The name of the Photon server:
continued. . .
-x position[%][r]
The x coordinate of the upper-left corner of the
window, in pixels, or as a percentage of screen
width if % is specified. If r is specified, the
coordinate is relative to the current console.
-y position[%][r]
The y coordinate of the upper-left corner of the
window, in pixels, or as a percentage of screen
height if % is specified. If r is specified, the
coordinate is relative to the current console.
By default, all these options are enabled so that users can dynamically
move or resize the application, or specify its initial state. For
example, to start an application in console 4 (the center of the
workspace), specify the command-line options:
-x100% -y100%
Run options
Here’s an example of the Run Options tab:
Run Arguments
Command line arguments used when PhAB runs your
application
Importing files
PhAB lets you import several types of files by using the Import Files
item in the file menu:
• PhAB modules
1 Choose Import Files from the File menu, then choose the
appropriate type from the Import Files submenu. You’ll see a
file selector.
2 The file selector displays the available files of the specified type
in the current directory.
• .bmp
• .gif
• .jpg
• .pcx
Exporting files
You can export the code used to create a module. To do this, select
File→Export. A fragment of C code that creates the widgets in the
currently selected module is written to the home directory in a file
called module.code.
In this chapter. . .
Module types 145
Anatomy of a module 145
Selecting a module 147
How modules are saved 148
Changing module resources 148
Creating a new module 148
Deleting a module 149
Iconifying modules 149
Displaying modules at run time 150
Finding lost modules and icons 152
Window modules 152
Dialog modules 153
Menu modules 155
Picture modules 164
Module types
PhAB provides a number of types of modules, each with a specific
usage. The module type is identified by:
• the extension of the file that PhAB creates for the module when
you generate your application’s code.
CAUTION: Module files are binary; don’t edit them with a text
! editor or you could damage them.
Anatomy of a module
PhAB displays each module as a window in its work area. Like
windows, modules have a set of controls in their frames.
Collapse button
Minimize button
Close button
These buttons iconify the module.
Test button (some modules only)
Like the Test item in the Work menu, this lets you switch
the module into test mode.
When the render flags for a a module’s title bar are off, PhAB
displays an area around the module that you can use to manipulate the
module. This is useful if you are designing embedded applications,
which usually have windows with no title bar and no decorations.
Selecting a module
To select a module that’s in the PhAB work area:
• Select the module in the Module Tree panel (this works for both
iconified and noniconified modules).
Whichever method you choose, you’ll see resize handles that indicate
the module is selected.
1 From the Project menu, choose a command for the module type
you want to create, which is one of:
• Add Window
• Add Dialog
• Add Menu
• Add Picture Module
2 For window and dialog modules, PhAB asks you to choose the
style from a dialog that displays the available choices.
3 Click on Done.
PhAB creates the module for you. You can change the default
instance name on the Resources control panel.
For more info on creating specific types of modules, see the sections
on each type of module in this chapter.
You can also import modules from other PhAB applications. For
more information, see “Importing files” in the Working with
Applications chapter.
Deleting a module
To delete a module:
Deleting a module doesn’t delete the module’s file; it just removes the
name from the list. Any callbacks belonging to the module or its
children are deleted.
Iconifying modules
PhAB’s work area lets you work on several application modules at
once. You can iconify modules to organize your work area. To reduce
any module in the work area to an icon:
Or:
Once it’s iconified, the module positions itself at the bottom of the
work area. You can drag it anywhere in the work area, so (for
example) you can group commonly used or related icons.
Positioning a module
You can specify where a module will display when you create a link
callback from a widget to that module. To do this, you use the
location dialog.
To open the Location dialog and select a module’s location:
Location dialog.
4 Click on Done.
• Choose Cascade from the Window menu. PhAB cascades all open
modules in the PhAB work area.
Window modules
Window modules can support multiple instances. That is, two or more
copies of the same window module can be displayed at the same time.
As a result, you should keep track of each window’s instance pointer,
which is generated when you create the window. That way, you’ll
always know which window you’re dealing with when you process
callbacks. For more information, see “Handling multiple instances of
a window” in the Working with Code chapter.
Even though your application’s base window is a window module,
you usually display it only once, at startup. So unless your application
needs to display more than one copy of the base window at the same
time, you don’t have to keep track of the base window’s instance
pointer.
For an example of code for handling multiple instances of window
modules, see “Creating Windows” in the Tutorials chapter.
Dialog modules
Dialog modules let you obtain additional information from the user.
Typically, you use this information to carry out a particular command
or task.
Predefined dialogs
The Photon libraries include convenience functions that define
various handy dialogs:
PtFileSelection()
Create a file-selection dialog
PtFontSelection()
Create a font-selection dialog
PtMessageBox()
Pop up a message box
PtPrintPropSelect()
Change the printing options for a selected printer
via a modal dialog
PtPrintSelection()
Display a modal dialog for selecting print options
Menu modules
inside it. Instead, you use PhAB’s menu editor to create the menu’s
items.
• provide a hotkey that directly invokes the command that the item
represents, even when the menu isn’t visible
Field Description
Item Text The text that will be displayed
Accel Text The hotkey to invoke the command
Inst Name The name used within the application code
Callback The function that will be called when the item is
selected
Image The icon to use for the menu item
1 Click on <NEW>.
3 In the Item Text field, enter the item’s text. To create a shortcut
key, place “&” in front of the character that will act as the
shortcut.
For example, let’s say you enter &File. In that case, the user
can select the item by pressing F.
4 In the Inst Name field, enter the instance name you’ll use.
5 If you plan to have a hotkey callback for this item, enter the
hotkey string and modifier key (for example, Ctrl-S) in the
Accel Text field. The hotkey is displayed in the menu as a
reminder to the user.
Field Description
Item Text The text that will be displayed
Inst Name The name used within the application code
1 Click on <NEW>.
3 In the Item Text field, type the name of the submenu. To create
a keyboard shortcut, place “&” in front of the character that will
act as the shortcut (just like command items, above).
4 Click on Apply.
1 Click on <NEW>.
3 Click on Apply.
Field Description
Item Text The text that will be displayed
Accel Text The hotkey to invoke the command
Inst Name The name used within the application code
Callback The function that will be called when the item is
selected
Image The icon to use for the menu item
Field Description
Function The function that will be called
1 Drag the Browse item until its outline is directly over Edit.
2 Release the mouse button. The Browse item appears in its new
position.
You could add the callback to the Pt_CB_ACTIVATE list, but adding
it to Pt_CB_ARM allows the user to access it in two ways:
• by pressing the left mouse button on the menu button widget,
dragging to highlight a menu item, and releasing to select it.
This is known as the press-drag-release (PDR) method.
• by clicking on the menu, and then clicking on a menu item
If you use an Activate callback, the user can only use the second
method.
4 Have the callback display the menu module. See “Module
callbacks” in the Editing Resources and Callbacks in PhAB
chapter.
5 If you need to initialize the menu whenever it’s displayed,
specify a setup function for it. See “Initializing menus” in the
Working with Code chapter.
If you want your menu to appear when you press the right mouse
button while pointing at a widget, you’ll need to use an internal link.
For more information, see the Accessing PhAB Modules from Code
chapter — there’s even an example.
Picture modules
Widget class File extension Widget creation
Not applicable .wgtp Directly from the widget palette
Displaying a picture
You always access picture modules from within your application
code. To access a picture, you must create an internal link to it. This
tells PhAB to generate a manifest that you can use with PhAB’s API
functions, such as ApCreateModule(), to access the picture.
For more information, see the Accessing PhAB Modules from Code
chapter.
In this chapter. . .
Types of widgets 169
Instance names 170
Creating a widget 172
Selecting widgets 174
Aligning widgets 179
Distributing widgets 181
Common User Access (CUA) and handling focus 181
Ordering widgets 185
Dragging widgets 186
Setting a widget’s x and y coordinates 187
Transferring widgets between containers 188
Resizing widgets and modules 188
Clipboard 189
Duplicating widgets and containers 192
Deleting widgets or modules 193
Matching widget resources and callbacks 193
Importing graphic files 195
Changing a widget’s class 196
Templates 196
Since widgets inherit a lot of behavior from their parent classes, you
should make yourself familiar with the fundamental classes:
PtWidget, PtBasic, PtContainer, and so on.
Types of widgets
There are two major types of widgets:
Instance names
If your program has to interact with a widget, that widget must have a
unique instance name. Using this name, PhAB generates a global
variable and a manifest that let you easily access the widget from
within your code.
To view or edit a widget’s instance name, use the Widget Instance
Name field at the top of the Resources or Callbacks control panel:
You can optionally include the instance name in the widget’s memory.
See “Other Generate options” in the Working with Applications
chapter.
• Leave the instance name equivalent to the class name (that is, leave
the default alone).
Or:
If you don’t want to create a unique instance name for a string that’s
to be translated, specify a single @ character for the instance name;
PhAB appends an internal sequence number to the end.
If you don’t want to create unique instance names, but you want to
organize the text for translation (say by modules), you can give the
strings the same instance name, and PhAB will append a sequence
number to it. For example, if you assign an instance name of @label
to several strings, PhAB generates @label, @label0, @label1, ...
as instance names.
Duplicate names
PhAB resets the instance name of a widget back to the widget class
name if it detects a duplicate name when you:
Creating a widget
To create a widget:
Widgets snap to the grid if it’s enabled. See “Grid preferences” in the
chapter on PhAB’s environment.
To improve your application’s performance, avoid overlapping
widgets that are frequently updated.
You can also create a widget by dragging its icon from the widget
palette to the Module Tree control panel. Where you drop the icon
determines the widget’s place in the family hierarchy.
3 Release Ctrl.
Selecting widgets
When PhAB is in select mode, the pointer appears as an arrow. To put
PhAB into select mode:
A single widget
To select a single widget, you can:
Point-and-click method
To select a single widget using point and click:
To select the parent of a widget, hold down Shift-Alt and click on the
widget. This is a handy way to select a PtDivider or PtToolbar.
You must press Shift and then Alt for this method to work.
Control-panel methods
The Next and Previous buttons in the Resources and Callbacks
control panels let you select any widget in the current module.
The Module Tree control panel displays a tree of all the widgets in the
module. Using this tree, you can:
Multiple widgets
To select multiple widgets, you can:
When you select two or more widgets, the Resources control panel
displays only the resources that those widgets have in common.
Editing any of these resources affects all the selected widgets.
PhAB uses two colors to show selected items if the Show Selection
option is selected in the View menu. The colors can be customized in
the Preferences dialog.
In the example above, the toggle widget is not selected, it just happens
to be in the same area as the selected widgets. The widget highlighted
by red is the first widget in the selection. The widgets highlighted by
blue are the rest of the widgets in the selection. If you use an align or
match command, the first selected widget is the source widget.
1 Position the pointer above and to the left of the widgets you
want to select.
3 Hold down the left mouse button, then drag the pointer down to
the right. You’ll see an outline “grow” on the screen.
4 When all the widgets are within the outline, release the mouse
button. You’ll see resize handles appear around the area defined
by the selected widgets.
The above methods for selecting multiple widgets work only for
widgets at the same hierarchical level. For example, let’s say you’ve
just selected two buttons inside a window. You can’t extend that
selection to include a button that’s inside a pane.
To remove the last widget from the current list of selected widgets:
2 Find the group in the tree and click on the widget’s name.
1 Click on any widget within the group to select the entire group.
Hidden widgets
If you can’t find a widget (it may be hidden behind another widget or
is outside the boundaries of its container), do the following:
For more information on the toolbars and control panels, see the
chapter on PhAB’s environment.
Aligning widgets
You can align several widgets to another widget or to their parent
container.
For simple alignments, select the Align icon from PhAB’s toolbar:
• Choosing the Align icon from PhAB’s toolbar, and then choosing
Alignment Tool from the menu
Or:
• Choosing Align from the Widget menu, and then choosing
Alignment Tool from the submenu
Or:
• Pressing Ctrl-A.
To another widget
When you use this method to align widgets, the widgets are aligned to
the first widget you select, which is highlighted differently from the
other widgets in the selection if the Show Selection option is set in the
View menu. To align to another widget:
To a parent container
To align widgets to their parent container:
Distributing widgets
You can quickly and easily distribute widgets horizontally or
vertically, to evenly space them out on the GUI. To do this:
Distributed widgets.
To go to the: Press:
Next widget Tab
Previous widget Shift-Tab
continued. . .
To go to the: Press:
First widget in the next container Ctrl-Tab
Last widget in the previous container Ctrl-Shift-Tab
Controlling focus
Use the following Pt_ARG_FLAGS flags to control focus for a
widget:
Pt_GETS_FOCUS
Make the widget focusable.
Pt_FOCUS_RENDER
Make the widget give a visual indication that it has focus.
Pt_BLOCK_CUA_FOCUS
Prevent the CUA keys from being used to enter the container.
However, if the user clicks inside the container, or a focus
function gives it focus, the CUA keys can then be used.
Pt_ENABLE_CUA
Give the parent widget the chance to control whether or not a
child container handles the CUA keys:
• If this flag is set, the widget’s code handles the CUA keys.
• If it isn’t set, the CUA keys are passed up the widget family
until an ancestor is found with this flag set. This ancestor (if
found) handles the keys.
Pt_ENABLE_CUA_ARROWS
The same as Pt_ENABLE_CUA, but it applies only to the arrow
keys.
Focus callbacks
All descendants of the PtBasic widget have the following callback
resources:
Focus-handling functions
The functions listed below deal with focus. They’re described in the
Photon Library Reference.
These functions don’t actually change which widget has focus; they
tell you where focus can go:
PtFindFocusChild()
Find the closest focusable child widget
PtFindFocusNextFrom()
Find the next widget that can get focus
PtFindFocusPrevFrom()
Find the previous widget that can get focus
You can use these routines to determine which widget has focus:
PtContainerFindFocus()
Find the currently focused widget in the same
family hierarchy as a widget
PtContainerFocusNext()
Give focus to the next Pt_GETS_FOCUS widget
PtContainerFocusPrev()
Give focus to the previous Pt_GETS_FOCUS widget
PtContainerGiveFocus() or PtGiveFocus()
Give focus to a widget — these routines are identical.
PtContainerNullFocus()
Nullify the focus of a widget
PtGlobalFocusNext()
Give focus to next widget
PtGlobalFocusNextContainer()
Give focus to another container’s widget
PtGlobalFocusNextFrom()
Give focus to the next widget behind the specified widget
PtGlobalFocusPrev()
Give focus to the previous widget
PtGlobalFocusPrevContainer()
Give focus to a widget in the previous container
PtGlobalFocusPrevFrom()
Give focus to widget previous to the specified widget
Ordering widgets
In PhAB, each widget exists in front of or behind other widgets. This
is known as the widget order, and you can see it when you overlap
several widgets. The order of the widgets dictates how you can use
the CUA keys to move between widgets.
If you’re not using PhAB, the widget order is the order in which the
widgets are created. To change the order, see “Ordering widgets” in
the Managing Widgets in Application Code chapter.
• Use the Module Tree control panel. The widgets for each container
are listed from back to front.
Or:
• Use Test mode and press Tab repeatedly to check the focus order.
The easiest way to reorder the widgets is to use the Module Tree
control panel — just drag the widgets around until they’re in the order
you want.
You can also use the Shift-select method to reorder the widgets:
You can also select one or more widgets and then use the Raise and
Lower icons to change the widget order:
Dragging widgets
Dragging a widget is the easiest way to move a widget in most
situations since it’s quick and fairly accurate:
Dragging preferences
There are several preferences that you can set for dragging (see the
“Customizing your PhAB environment” section in the chapter on
PhAB’s environment):
• Dragging has a damping factor that determines how far you must
drag before the widget moves. The default is 4 pixels.
3 Move the pointer into the other container and click the mouse
button.
Clipboard
PhAB’s clipboard lets you cut, copy, and paste widgets and modules
between PhAB instances. You can’t use this clipboard with other
applications. To do use the clipboard, open two PhAB instances, copy
or cut something into clipboard in one instance, and then paste it into
the other instance.
You’ll find the clipboard helpful for these reasons:
Whenever you cut or copy, PhAB deletes any widgets already in the
clipboard.
Pasting
A paste operation copies widgets from the clipboard into a module.
To paste the contents of the clipboard:
• The instance names of the new widgets are reset to be the widget
class name.
If you want put the widgets or module somewhere else, you should
cut them, not delete them. For more information, see the section on
the clipboard in this chapter.
If you enable the Show Selection option in the View menu, the widget
selected first highlighted with a different color than other selected
widgets.
Templates
A template is a customized widget, group or hierarchy of widgets that
you want to use as the basis for other widgets. Templates are useful
when you want to create many widgets that look and behave alike.
PhAB automatically loads your templates, so you can easily create
instances of your widgets in any application.
You can build and save your own collection of template widgets.
Templates can be customized buttons, labels, sliders, backgrounds,
standard dialogs and windows. You can include callbacks in
templates.
Customized templates are not any different from the standard
templates in the Widgets palette. In fact, when you create a template,
you save it as a personal template (visible only to you) or as a global
PhAB template (visible in all PhAB instances).
Creating templates
To create a template:
Enter a folder name and select its type: User folder or PhAB
folder. A User folder is visible only to you and cannot be shared
with other PhAB users. A PhAB folder can be shared by several
PhAB users. The predefined Widgets folder is a PhAB folder.
6 Optionally, set the background color for the icon in the widget
palette, and the resizing method (use original dimension or
resize by dragging).
callbacks are saved. If the widgets you are saving don’t have
any callbacks attached, the Edit Callbacks button is disabled.
You can specify whether PhAB prompts you with a list of
included callbacks when you instantiate a template widget that
contains callbacks. This setting is set on the General tab of the
Preferences dialog under the When created widgets contain
callbacks option. If you select Automatically add callbacks,
all callbacks are added. If you select Ask me, PhAB prompts
you with a list of callbacks that you can select from.
2 Enter the name of the new widget class and click Continue.
PhAB scans the palette definition files for the widget’s
description table. Palette definition files (*.pal are listed in
4 Customize the newly created widget, customize it, and use the
Save Template to save the template.
Editing templates
You can change a template definition at any time by editing the
templates. To edit an existing template:
Deleting templates
To delete a template:
In this chapter. . .
Editing widget resources 205
Pixmap editor 206
Color editor 212
Flag/choice editor 215
Font editor 216
List editor 218
Number editor 220
Text editors 221
Code editor 224
Layout editors 225
Callbacks 231
Editing callbacks 233
Module callbacks 235
Code callbacks 238
Hotkey callbacks 239
Event handlers — raw and filter callbacks 245
When you select two or more widgets, the Resources control panel
displays only the resources that those widgets have in common.
Editing any of these resources affects all the selected widgets.
PhAB’s default value for a resource isn’t necessarily the default value
assigned by the widget itself.
continued. . .
Pixmap editor
The pixmap editor lets you customize a widget’s pixmap. The editor
provides a complete range of tools, so you can draw virtually any
pixmap your application might need.
✸ To open the pixmap editor for any widget that can contain an
image (for example, PtLabel, PtButton), click on the
widget, then click on a Image resource in the Resources control
panel.
The editor has several drawing modes and tools, which are described
in the sections that follow. The default is freehand mode—you simply
drag the pointer across the drawing grid.
If you reduce the size of a pixmap, part of the image may be cut off.
Choosing colors
To choose the draw color:
Drawing freehand
The freehand tool lets you draw freeform lines and erase single pixels
for quick fix-ups.
To draw in freehand mode:
✸ To erase the pixel under the pointer, click the right mouse
button.
3 Drag the pointer to where you’d like the object to end, then
release the mouse button.
You can repeat this step as often as you’d like.
2 Move the pointer inside the area you wish to fill, then click.
If an outline area has a break, the fill operation spills out of the hole
and might fill the entire pixmap display.
Selecting an area
To use some tools, you first select an area of the pixmap.
To select an area:
1 Click on the Select tool:
Nudging an area
To nudge a selected area one pixel at a time:
1 Select the area you wish to nudge.
2 Click a nudge arrow or press an arrow key:
You can select an area and then use these tools to rotate, flip, cut,
copy, clear, or paste the area.
Some things to note:
• If you rotate an area that isn’t perfectly square, the area may
overwrite some pixels.
• If part of the rotated area falls out of the pixmap, that part may be
deleted.
• By using the flip tools with the copy tool, you can create mirror
images.
• When you paste, point to the new location, then click. This
position is the top-left corner of the pasted area.
• You can use the pixmap clipboard to copy images from one widget
to another, or copy an image to its “set” version to make minor
modifications.
• You can quickly clear the whole image by clicking the Clear
command when nothing is selected.
Color editor
The color editor lets you modify any color resource. Where you click
in the Resource control panel determines which color editor you see:
• transparent
To change the value, move the slider on the left. To change the hue,
click or drag in the color patch on the right.
Flag/choice editor
Whenever you click on a flags resource or on a resource that lets you
select only one of several preset values, you’ll see the flag/choice
editor. For example:
Flag/Choice editor.
Flag resources
If you click on a flag resource, this editor lets you make multiple
selections from the displayed list.
To edit a flag list:
1 Select (or deselect) the flags you want. Since each flag is a
separate toggle, you can select any number of flags or leave
them all unselected. Some flags contain groups of mutually
Font editor
Whenever you select any font resource in the Resources control panel
you’ll see the font editor:
Font editor.
List editor
Widgets such as PtList provide a list of text-based items. To edit the
list, you use PhAB’s list editor.
To open the editor and add the first item:
List editor.
2 Click on the text field near the bottom of the dialog, then type
the text you want.
If you need to type characters that don’t appear on your keyboard, you
can use the compose sequences listed in “Photon compose sequences”
in the Unicode Multilingual Support appendix.
Number editor
You can edit the value of a numeric resource right in the Resources
control panel, or you can click on the resource name to use the
number editor:
Number editor.
Text editors
You can edit a text resource right in the Resources control panel, or
you can click on the resource to display a text editor. There are two
text editors: one for single-line text, and one for multiline.
Whenever you click on a single-line text resource in the Resources
control panel (e.g. the Text String resource for PtText), you’ll see
the text editor:
Text editor.
When you select any multiline text resource—such as the Text String
resource of a PtLabel or PtMultiText widget—you’ll see the
multiline text editor:
The single-line and multiline editors are similar — here are the
common operations:
continued. . .
If you need to type characters that don’t appear on your keyboard, you
can use the compose sequences listed in “Photon compose sequences”
in the Unicode Multilingual Support appendix.
Code editor
When you select a function resource, such as the Draw Function
(Pt_ARG_RAW_DRAW_F) resource of a PtRaw widget, you’ll see
the Code editor:
Code editor.
The widget must have a unique instance name before you can edit its
function resources.
Layout editors
When you set the Layout Type resource Pt_ARG_LAYOUT_TYPE of
a container widget to something other than the default
Pt_ANCHOR_LAYOUT, you can then set the corresponding Layout
Info resource. You can also set the corresponding Layout Data
resource for widgets within the container widget. Each kind of Layout
Info and Layout Data resource has an editor.
For more information about layouts, see Using layouts in the
Geometry Management chapter.
• additional flags
• grid margins.
For more information about these callbacks, see the Widget Reference.
If you’re interested in using Pt_CB_MENU to display a menu
module, see the Accessing PhAB Modules from Code chapter.
All Photon widgets inherit two other types of callbacks:
Hotkey callbacks
Attach callback code to a key or keychord. When the
application window gets focus, the hotkeys become active.
Pressing one invokes the appropriate hotkey link callback.
Editing callbacks
The callback editor lets you add, change, delete, or view a widget’s
list of callbacks.
To add a callback to a command item or toggle item in a menu, see
“Menu modules” in the Working with Modules chapter.
2 Choose the callback type from the widget’s callback list. (For
example, to add an Pt_CB_ACTIVATE callback, click on
Activate.)
Callback editor.
• hotkey callbacks
• event handlers (raw and filter callbacks)
Module callbacks
A module-type link callback can be used to connect a widget to a
module. For example, selecting a button could create a module.
Name The name of the module. If you click on the icon next to
this field, you’ll see a list of existing modules. Either
choose from this list or enter the name of a module that
doesn’t exist (PhAB creates the module for you when
you add the callback).
The setup function for a menu module is called only before the menu
is displayed. For most applications, you would use this function to set
the initial states of the menu items. For example, you could use it to
disable certain menu items before the menu is displayed.
Code callbacks
This type of callback lets you run a code function when the widget’s
callback is invoked.
You can add code callbacks from your application’s code, but it’s
easier to do in PhAB. For more information, see “Callbacks” in the
Managing Widgets in Application Code chapter.
Hotkey callbacks
Widgets support hotkey callbacks. These callbacks let you attach
keyboard keys to specific callback functions. When the application
window gets focus, the hotkeys become active. Pressing one invokes
the appropriate hotkey link callback.
• You can use a modifier on its own as a hotkey, but it’s probably not
a good idea.
• A hotkey is processed after the widgets have been given the key
event. If a widget consumes the event, no hotkey callback is called.
So when a text field has focus, the Enter key, arrow keys, Space,
You must fill in the Hotkey field when creating a hotkey callback.
There are two ways to set up the hotkey: one easy, the other not so
easy.
Use lowercase letters for hotkeys; uppercase letters won’t work. For
example, for a hotkey Alt-F, look up the hex value for Pk_f, not
Pk_F.
The field also has 3 toggle buttons—Ctrl, Shift, and Alt—to let
you specify modifiers for the hotkey value.
• the easy way—press the button to the right of the Alt toggle
button, then press the keychord you want to use for the hotkey.
PhAB automatically determines the key and modifiers you pressed.
Processing hotkeys
Here’s how a hotkey works:
Pt_HOTKEY_TERMINATOR
Prevent the hotkey search from going up to the parent container.
Disabling hotkeys
Giving the user a visual indication that a hotkey is disabled is
different from actually disabling the hotkey.
To give the visual indication, use the technique appropriate to the
widget:
• Don’t disable the hotkey. Instead, as the first thing you do in your
hotkey’s callback code, check to see if anything should be done. If
not, just return from the callback. For example, if the hotkey
callback is the one for pasting text, check to see if there’s anything
to paste. If there isn’t, simply return.
Or
• With the exception of disjoint widgets, if the widget that the hotkey
callback is attached to isn’t selectable, the hotkey is treated as if it
didn’t exist. For a widget to be selectable, the Pt_SELECTABLE
flag must be set in the Pt_ARG_FLAGS resource.
One good reason for this approach is that it works even if your
application has the same hotkey defined in more than one window.
For example, we might have an Edit menu in the base window and
an Erase button in a child window, both with Alt-E as a hotkey. If
the child window currently has focus and the user presses Alt-E,
the child window’s Erase button callback is called.
Now, if we disable the Erase button in the child window, we would
want Alt-E to cause the base window’s Edit menu to appear. In this
scenario, as long as the Erase button is selectable, its callback
would be called. So we simply make the Erase button
unselectable. Now when the user presses Alt-E, the base window’s
Edit menu appears, even though the child window still has focus.
Or
• You could call PtRemoveHotkeyHandler() to remove the hotkey
and later call PtAddHotkeyHandler() to enable it again.
Pt_CB_FILTER
Invoked before the event is passed to the widget.
For a description of raw and filter event handlers and how they’re
used, see “Event handlers — raw and filter callbacks” in the Events
chapter.
For information on adding event handlers in application code, see
“Event handlers” in the Managing Widgets in Application Code
chapter.
The Event Mask field lets you specify which Photon events you
want the widget to be sensitive to. When any of those low-level
events occur, the widget invokes the callback.
Click on the icon next to this field to open the event selector:
Event selector.
4 Select the events you want the widget to be sensitive to, then
close the selector.
For more info, see the event types described for the PhEvent_t
structure in the Photon Library Reference.
In this chapter. . .
Container widgets 249
Geometry negotiation 249
Absolute positioning 255
Aligning widgets using groups 256
Constraint management using anchors 261
Using layouts 266
Enforcing position or size constraints without anchors or layouts
294
Container widgets
A container widget is a child of the PtContainer widget class.
Container widgets are the only widgets that can have children. Any
widget that doesn’t have a window of its own is always rendered
within the boundaries of its parent. Only widgets belonging to a
subclass of the PtWindow widget class get a window of their own.
Container widgets are responsible for performing geometry
management. A container widget’s primary responsibility is to
position each child and size itself appropriately to accommodate all
its children at their desired locations. The container may also impose
size constraints on its children (for example, forcing them all to be the
same size). The container must also constrain the children so that they
don’t appear outside the container’s boundaries. This is normally
done by clipping the child.
To understand how different containers handle geometry
management, it’s important to understand the geometry of a widget.
See “Widget Geometry” in the Introduction to this guide.
Geometry negotiation
When a widget is realized, a geometry negotiation process is initiated
in all the widgets in the widget family hierarchy. Each child of the
widget is given the opportunity to calculate its size. This ripples down
through all the widgets in the family, resulting in a calculation of the
size of each of the descendants first.
Once each child has calculated its desired size, the parent widget may
attempt to determine the layout for its children. The layout that the
widget performs depends on:
If the application has specified a size for the widget, then it may
choose to lay out the children using only that available space. This is
influenced by the resize policy set for the widget. The
Pt_ARG_RESIZE_FLAGS resource is a set of flags that determine the
resizing policy for the widget. The flags specify a separate resizing
policy for the width and height of the widget. If no policy is specified
for either dimension, the widget doesn’t attempt to resize itself in that
dimension when performing the layout. Any other resize policy
allows the widget to grow in that dimension to accommodate its
children. For more details, see “Resize policy,” below.
If the widget doesn’t have a predetermined size, it tries to size itself to
accommodate all the children using the appropriate layout policy. It
does so by first attempting to determine a correct layout and then
determining the space required to accommodate it.
The layout process determines the desired location of each child. The
layout policy used by the widget controls how the layout attempts to
position the children relative to each other. It must take into account
the dimensions of the children. The container is responsible for fixing
the position of each child, so the layout policy may choose whether or
not to take into account the position attributes of the children.
In performing the layout, the widget may also take the resize policy
into account. Based on this policy, it determines whether it must
adjust its width or height, or change the layout to account for space
restrictions. The widget tries to choose a layout that best meets the
constraints imposed by any size restrictions and by the layout policy.
After determining the desired position of each of its children, the
widget calculates the width and height it needs to accommodate the
children at these locations. It changes its dimensions, if necessary, to
fit each of the children at the desired position. If this isn’t possible
because the resize policy doesn’t allow it, the widget recalculates the
positions to fit the children within the space available.
Resize policy
Any change to a widget that may affect the amount of space required
to display its contents can result in the widget’s resizing itself to fit its
contents. This is governed by the resize policy enforced by the widget.
The resize policy affects both basic widgets and containers. A
container checks its resize policy when it lays out its children to
determine whether it should resize itself to accommodate all the
children at their desired locations. Through the geometry negotiation
process, this effect is propagated up the widget family until the size of
the window widget is determined.
The Pt_ARG_RESIZE_FLAGS resource controls the resize policy.
This resource consists of a separate set of flags for the width and the
height. The values of the flags determine the conditions under which
the widget recalculates the corresponding dimension. The values are
checked whenever the widget is realized or its contents change.
Pt_RESIZE_X_ALWAYS
Recalculates the widget’s size whenever the value of the x
dimension changes. The widget grows or shrinks horizontally
as its contents change.
For example, the following figure shows a button with the
Pt_RESIZE_X_ALWAYS flag set as the label changes from
Hello to Hello, world to Hi:
Pt_RESIZE_Y_ALWAYS
Recalculates the widget’s size whenever the value of the y
dimension changes. The widget grows or shrinks vertically as
its contents change.
Pt_RESIZE_XY_ALWAYS
Recalculates the widget’s size whenever the value of the x or y
dimension changes. The widget grows or shrinks in both
directions as its contents change.
Pt_RESIZE_Y_AS_REQUIRED
Recalculates the widget’s size whenever the y dimension
changes and no longer fits within the current space available.
Pt_RESIZE_XY_AS_REQUIRED
Recalculates the widget’s size whenever the x or y dimension
changes and no longer fits within the current space available.
These flags may also be modified by the values of another set of flags,
namely:
• Pt_RESIZE_X_INITIAL
• Pt_RESIZE_Y_INITIAL
• Pt_RESIZE_XY_INITIAL
If you set any of these “initial” flags, the widget doesn’t resize in
response to a change in the data — it changes its size only during the
geometry negotiation process whenever it’s realized. The widget
either makes itself exactly the right size for its contents, or grows to fit
its contents if the dimensions it has at the time aren’t large enough.
If none of the resize flags is set, the widget doesn’t try to calculate its
own dimensions, but uses whatever dimensions have been set by the
application (thereby possibly clipping the widget’s contents as a
result).
For example, the following figure shows a button with no resize flags
set as the label changes from Hello to Hello, world to Hi.
• Pt_RESIZE_X_BITS
• Pt_RESIZE_Y_BITS
• Pt_RESIZE_XY_BITS
For example, to make a container grow to fit all its children if it isn’t
large enough when it’s realized, set both the initial and required resize
flags for x and y:
PtSetResource (ABW_my_container, Pt_ARG_RESIZE_FLAGS,
(Pt_RESIZE_XY_INITIAL|Pt_RESIZE_XY_AS_REQUIRED),
Pt_RESIZE_X_BITS|Pt_RESIZE_Y_BITS);
There are also some constants that simplify the setting of these flags.
For example, there’s a constant representing the bitmask for setting
both the x and y flags simultaneously, and there are constants for each
flag with the x or y shift applied. All these constants are defined in the
<photon/PtWidget.h> header file.
Absolute positioning
The most basic form of layout a container can provide is to position
its children without imposing any size or positioning constraints on
them. In such a situation, a child widget is pinned to a particular
location within the container, and the container doesn’t change its
size.
A widget employing this layout policy is somewhat analogous to a
bulletin board. You can pin items to the bulletin board, and they stay
wherever they’re pinned. All container widgets can perform absolute
positioning.
The easiest way to position and size each child is to use the mouse in
PhAB.
To position each of the children from your application’s code, you
must set the Pt_ARG_POS resource for each child. If the widgets
must be a consistent or predetermined size, you must also set the
Pt_ARG_DIM resource for each child. The position you specify is
relative to the upper left corner of the parent’s canvas, so you can
disregard the parent’s margins when positioning children.
1 Select the widgets using either a bounding box or the “Shift and
click” method (as described in the Creating Widgets in PhAB
chapter).
You should use “Shift–click” if you plan to align the widgets in
order using the Orientation resource. The first widget you select
becomes first within the group. If order isn’t important or
alignment isn’t required, the bounding box method works just
fine.
When first realized, the group widget initially sizes itself to be large
enough to hold all the children after they’ve been arranged.
The last row or column may have fewer widgets than the others.
When the elements of a group are laid out in rows and columns, the
widgets themselves may either be tightly packed or they may be
spaced out equally as rows and/or columns. This is controlled by the
Pt_ARG_GROUP_SPACING resource.
Pt_GROUP_EXCLUSIVE
Allow only one child to be set at a time. This flag can be used to
make a group of toggle buttons into radio buttons (that is, a set
of mutually exclusive choices).
Pt_GROUP_EQUAL_SIZE
Lay out the widgets in a grid, using a cell size chosen by the
group based on the width of the widest child and the height of
the tallest child. The dimensions of all the children are set to
this size when they’re laid out.
Pt_GROUP_EQUAL_SIZE_HORIZONTAL
Make all the children the width of the widest one.
Pt_GROUP_EQUAL_SIZE_VERTICAL
Make all the children the height of the tallest one.
Pt_GROUP_NO_SELECT_ALLOWED
Set this flag for an exclusive group if it’s valid not to have any
child set. The user can unselect the currently set child by
clicking on it again.
Pt_GROUP_NO_KEYS
Don’t allow the user to move inside the group by pressing the
arrow keys.
Pt_GROUP_NO_KEY_WRAP_HORIZONTAL
Don’t wrap around to the other side of the group when using the
left and right arrow keys.
Pt_GROUP_NO_KEY_WRAP_VERTICAL
Don’t wrap around to the top or bottom of the group when
using the up and down arrow keys.
Pt_GROUP_STRETCH_VERTICAL
Stretch the bottom row of widgets as the group expands.
Pt_GROUP_STRETCH_HORIZONTAL
Stretch the right column of widgets as the group expands.
Pt_GROUP_STRETCH_FILL
Stretch the last widget(s) to fill the available space in the
direction indicated by the orientation.
When using PhAB, you don’t specify anchor offsets. Instead you
position the widgets at the desired offset by setting the position
(Pt_ARG_POS) and dimension (Pt_ARG_DIM) resources. PhAB
calculates the anchor offsets automatically, based on the relative sizes
and positions of the parent and the anchored child.
You can turn anchoring on or off using the Anchoring on/off button
on the PhAB toolbar. If a parent widget has children that are
anchored, and you want to change the size of the parent widget
without affecting size of the children, turn anchoring off.
The width of the child widget is influenced by the anchors for its left
and right sides; the height is influenced by the anchors for the top and
bottom. If either of an opposing pair of edges is allowed to float, the
constraints are met by altering only the position of the widget in the
corresponding dimension. This means that the widget may slide in
any of the four directions to satisfy the anchor constraints. If both
edges are anchored, the widget must be resized in that dimension as
well.
Example of anchoring.
that has a group widget for the work area, we can identify the types of
anchors necessary to make it resize correctly in response to a change
in the size of the window widget.
Each edge of the work area is anchored to the corresponding edge of
the window. The left and top anchor offsets are set to be the same as
the position attribute for the widget. This must be calculated to sit
below the menu bar. The dimensions of the widget are set to the
desired amount of work space.
When realized, the window positions the work area at the location
specified by its position attribute. The window’s size is set to be large
enough to contain the work area.
If the window is resized, the width and height of the work area are
resized accordingly, since all the edges are anchored. If the anchor
offsets were specified correctly, the position of the widget aren’t
altered.
We don’t have to do anything for the menu bar, because it’s
automatically anchored to the top and sides of the window.
Anchor resources
The Pt_ARG_ANCHOR_FLAGS resource (defined by PtWidget)
controls the anchor attachments. Within the anchor flags, there are
three flags associated with each edge of the widget; these three flags
allow each edge to be anchored in one of two possible ways:
• Pt_LEFT_ANCHORED_LEFT
• Pt_LEFT_ANCHORED_RIGHT
• Pt_RIGHT_ANCHORED_LEFT
• Pt_RIGHT_ANCHORED_RIGHT
• Pt_TOP_ANCHORED_BOTTOM
• Pt_TOP_ANCHORED_TOP
• Pt_BOTTOM_ANCHORED_BOTTOM
• Pt_BOTTOM_ANCHORED_TOP
So to set the left and right edge for our menu bar in the example
above, the argument list element would be initialized as follows:
PtSetArg(&arg[n], Pt_ARG_ANCHOR_FLAGS,
Pt_LEFT_ANCHORED_LEFT|Pt_RIGHT_ANCHORED_RIGHT|
Pt_TOP_ANCHORED_TOP,
Pt_LEFT_IS_ANCHORED|Pt_RIGHT_IS_ANCHORED|
Pt_TOP_IS_ANCHORED);
When setting anchor flags from your application’s code, all the anchor
offsets are specified using the Pt_ARG_ANCHOR_OFFSETS
resource. This resource takes a PhRect_t structure (see the Photon
Library Reference) as a value. The upper left corner of the rectangle
is used to specify the anchor offset for the top and left edges of the
widget, and the lower right corner of the rectangle indicates the
anchor offset for the right and bottom edges.
Using layouts
If you wish to maintain more complex relationships among the
positions of children relative to the container, or relative to each other,
consider using layouts. A layout is a property of container widgets
that sizes and organizes its children when the container or its children
change size.
You set layouts using resources in the container widget. You can
refine the way layouts place widgets by setting resources on the child
widgets. Below are the resources involved in using layouts.
PtContainer has these layout-related resources:
Pt_ARG_LAYOUT
This is a generic resource that lets you set the active layout of
the container and optionally the layout information structure.
Here’s an example of setting both for a fill layout:
PtArg_t args[20];
int i = 0;
PtFillLayoutInfo_t info;
info.type = Pt_LAYOUT_HORIZONTAL;
info.spacing = 2;
PtSetArg( &args[i++], Pt_ARG_LAYOUT, PtFillLayout, &info );
Pt_ARG_LAYOUT_INFO *
This is a generic resource to specify layout info, which you can
use to set or get any of the Pt_ARG_*_LAYOUT_INFO
resources.
Pt_CB_LAYOUT
This callback is called when the layout is about to start laying
out children and when it’s finished. The
cbinfo->reason_subtype indicates which situation called it, and
is one of:
• Pt_LAYOUT_INIT — layout is about to start laying out
• Pt_LAYOUT_DONE — layout has finished laying out
children.
You can use this callback to fine-tune the layout procedure.
Pt_ARG_LAYOUT_TYPE
This is an alternative method of setting or getting the active
layout type and optionally the layout information structure
(Pt_ARG_*_LAYOUT_INFO). Here’s an example of setting
both for a fill layout:
PtArg_t args[20];
int i = 0;
PtFillLayoutInfo_t info;
info.type = Pt_LAYOUT_HORIZONTAL;
info.spacing = 2;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_TYPE, Pt_FILL_LAYOUT,
&info );
Pt_ARG_FILL_LAYOUT_INFO *
Used only to set the PtFillLayoutInfo_t structure for the
PtFillLayout layout type.
Pt_ARG_ROW_LAYOUT_INFO *
Used only to set the PtRowLayoutInfo_t structure for the
PtRowLayout layout type.
Pt_ARG_GRID_LAYOUT_INFO *
Used only to set the PtGridLayoutInfo_t structure for the
PtGridLayout layout type.
Pt_ARG_LAYOUT_DATA *
This is the generic method of setting/getting layout data for a
widget.
Pt_ARG_ROW_LAYOUT_DATA *
Used only to set the PtRowLayoutData_t structure for the
PtRowLayout layout type.
Pt_ARG_GRID_LAYOUT_DATA *
Used only to set the PtGridLayoutData_t structure for the
PtGridLayout layout type.
Pt_ARG_EFLAGS
If this resource has Pt_SKIP_LAYOUT set, the container doesn’t
apply the layout to the widget. This is useful if you have
widgets that you want to place by hand.
PtFillLayout
This is a simple type of a layout. It organizes children in one row or
one column and makes them the same size. The width of each child
will be at least as wide as the widest child’s width. The same rule
applies to the height.
You can set the layout’s options using the layout information
structure, PtFillLayoutInfo_t. See the PtContainer resource
Pt_ARG_FILL_LAYOUT_INFO for a description of the
PtFillLayoutInfo_t structure.
There is no layout data structure for the Pt_ARG_LAYOUT_DATA
resource of children for this layout type.
Let’s take a look at this example:
/* fill_layout.c example */
#include <Pt.h>
int
main( int argc, char *argv[] )
{
PtWidget_t *window;
PtArg_t args[20];
int i = 0;
PtFillLayoutInfo_t info;
/* Create a window */
i = 0;
PtSetArg( &args[i++], Pt_ARG_WINDOW_TITLE, "PtFillLayout",
0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT, PtFillLayout, &info
);
if( NULL == ( window = PtAppInit( NULL, &argc, argv, i,
args ) ) ) {
perror( "PtAppInit()" );
return 1;
}
/* Create buttons */
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Input (stdin)",
0 );
PtCreateWidget( PtButton, window, i, args );
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Output (stdout)", 0
);
PtCreateWidget( PtButton, window, i, args );
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Error (stderr)", 0
);
PtCreateWidget( PtButton, window, i, args );
PtRealizeWidget( window );
PtMainLoop();
return 0;
}
PtRowLayout
The row layout is similar to the fill layout but it has a very important
difference — it can wrap, so it can place children in more than one
row or column. When the flags member of the PtRowLayoutInfo_t
structure has the Pt_ROW_WRAP flag set, and there is not enough
space in the row to fit the next child, the child is moved to the
beginning of the next row. The row layout also has margins (the space
around all the widgets) and the children of the container can have
their own data (in their Pt_ARG_LAYOUT_DATA resources) which
can be used to fine-tune the layout.
See the PtContainer resource Pt_ARG_ROW_LAYOUT_INFO for
a description of the PtRowLayoutInfo_t structure. The
PtRowLayoutData_t structure is described in the PtWidget
resource Pt_ARG_ROW_LAYOUT_DATA.
Let’s take a look at an example:
#include <Pt.h>
int
main( int argc, char *argv[] )
{
PtWidget_t *window, *b1, *b2, *b3;
PtArg_t args[20];
int i = 0;
PtRowLayoutInfo_t info;
/* Create a window */
i = 0;
PtSetArg( &args[i++], Pt_ARG_WINDOW_TITLE, "PtRowLayout",
0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT, PtRowLayout, &info
);
if( NULL == ( window = PtAppInit( NULL, &argc, argv, i,
args ) ) ) {
perror( "PtAppInit()" );
return 1;
}
/* Create buttons */
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Input", 0 );
b1 = PtCreateWidget( PtButton, window, i, args );
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Output (stdout)", 0
);
b2 = PtCreateWidget( PtButton, window, i, args );
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Err", 0 );
b3 = PtCreateWidget( PtButton, window, i, args );
PtRealizeWidget( window );
PtMainLoop();
return 0;
}
When Pt_ROW_PACK is not set, all the buttons are the same size:
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Err", 0 );
b3 = PtCreateWidget( PtButton, window, i, args );
with:
Initial window.
After stretching.
After shrinking.
What happened? The Err button started its own row because the
Pt_ROW_WRAP_BEFORE flag was set. Then the Err button was
stretched to fill all the extra space because the Pt_ROW_FILL flag was
set.
PtGridLayout
The Grid Layout is a very powerful and widely used layout. This
layout arranges children in a grid, which is why it’s sometimes called
a “table layout.” The layout info structure PtGridLayoutInfo_t
contains a number of members that adjust the layout operation.
Children of the layout container can have layout data
(PtGridLayoutData_t) attached to them to fine-tune the layout.
See the PtContainer resource Pt_ARG_GRID_LAYOUT_INFO for
a description of the PtGridLayoutInfo_t structure. The
PtGridLayoutData_t structure is described in the PtWidget
resource Pt_ARG_GRID_LAYOUT_DATA.
#include <Pt.h>
int
main( int argc, char *argv[] )
{
PtWidget_t *window;
PtArg_t args[20];
int i = 0;
PtGridLayoutInfo_t info = PtGridLayoutInfoDflts;
PtGridLayoutData_t data = PtGridLayoutDataDflts;
info.n_cols = 3;
info.flags = 0;
data.flags = Pt_H_ALIGN_BEGINNING;
/* Create a window */
i = 0;
PtSetArg( &args[i++], Pt_ARG_WINDOW_TITLE, "PtGridLayout",
0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT, PtGridLayout, &info
);
if( NULL == ( window = PtAppInit( NULL, &argc, argv, i,
args ) ) ) {
perror( "PtAppInit()" );
return 1;
}
/* Create buttons */
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "B1", 0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout
);
PtCreateWidget( PtButton, window, i, args );
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Button 2 (two)", 0
);
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout
);
PtCreateWidget( PtButton, window, i, args );
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Butt 3", 0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout
);
PtCreateWidget( PtButton, window, i, args );
i = 0;
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Butt 5", 0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout
);
PtCreateWidget( PtButton, window, i, args );
PtRealizeWidget( window );
PtMainLoop();
return 0;
}
Let’s see how spans work. Set the horizontal span of Butt 5 to 2
columns. To do that, add this code before Butt 5 is created:
data.flags = Pt_H_ALIGN_FILL;
data.h_span = 2;
Let’s test the vertical span. Undo the changes for the previous
example and add the following code before the Butt 3 widget is
created:
data.flags = Pt_V_ALIGN_FILL;
data.v_span = 2;
and add the following code after the widget creation call:
data = PtGridLayoutDataDflts;
The result:
Finally, let’s see how the grab flags work. If we resize the window we
get this:
We get this:
1 2 3
Module: 1
File: Browse... 2
Comment: 3
Type: CPU 4
Target: 5
Grows
down
Icon:
6
Location:
10
Save 11
Grows to Right-aligned
the right
int
main( int argc, char *argv[] )
{
PtWidget_t *window, *w, *ctnr;
PtArg_t args[20];
int i = 0;
PtGridLayoutInfo_t info;
PtGridLayoutData_t data;
info = PtGridLayoutInfoDflts;
info.n_cols = 3;
info.margin.ul.x = info.margin.ul.y = 5;
info.margin.lr.x = info.margin.lr.y = 5;
/* Create a window */
i = 0;
PtSetArg( &args[i++], Pt_ARG_WINDOW_TITLE, "Module Config", 0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT, PtGridLayout, &info );
if( NULL == ( window = PtAppInit( NULL, &argc, argv, i, args ) ) ) {
perror( "PtAppInit()" );
return 1;
}
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Module:", 0 );
PtCreateWidget( PtLabel, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_FILL | Pt_H_GRAB;
data.h_span = 2;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "phlocale", 0 );
PtCreateWidget( PtText, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "File:", 0 );
PtCreateWidget( PtLabel, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_FILL | Pt_H_GRAB;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "/usr/photon/bin/phlocale", 0 );
PtCreateWidget( PtText, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_FILL | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Browse...", 0 );
PtCreateWidget( PtButton, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Comment:", 0 );
PtCreateWidget( PtLabel, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_FILL | Pt_H_GRAB;
data.h_span = 2;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Type:", 0 );
PtCreateWidget( PtLabel, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_FILL | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "<module
type>", 0 );
w = PtCreateWidget( PtComboBox, window, i, args );
{
const char *list[] = { "Binary", "Config", "Development", "Support" };
PtListAddItems( w, list, sizeof(list)/sizeof(list[0]), 1
);
}
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_CENTER | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "CPU", 0 );
PtCreateWidget( PtLabel, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Target:", 0 );
PtCreateWidget( PtLabel, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_FILL | Pt_V_ALIGN_CENTER;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "<target>",
0 );
w = PtCreateWidget( PtComboBox, window, i, args );
{
const char *list[] = { "OS targets", "GUI target", "DEV target" };
PtListAddItems( w, list, sizeof(list)/sizeof(list[0]), 1 );
}
data = PtGridLayoutDataDflts;
data.flags = Pt_V_ALIGN_FILL | Pt_H_ALIGN_FILL;
data.v_span = 5;
data.hint.h = 30; // This is important to keep the list "in bounds"
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, 0 );
w = PtCreateWidget( PtList, window, i, args );
{
const char *list[] = { "arm", "mips", "sh", "x86" };
PtListAddItems( w, list, sizeof(list)/sizeof(list[0]), 1 );
}
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END | Pt_V_ALIGN_BEGINNING | Pt_V_GRAB;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Icon:", 0 );
PtCreateWidget( PtLabel, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_ALIGN_FILL_BOTH | Pt_GRAB_BOTH;
data.v_span = 3;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "<img>", 0 );
PtCreateWidget( PtButton, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_BEGINNING | Pt_V_ALIGN_BEGINNING;
data.margin.ul.x = 10;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Add", 0 );
PtCreateWidget( PtButton, window, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_BEGINNING | Pt_V_ALIGN_BEGINNING;
data.margin.ul.x = 10;
i = 0;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Delete", 0 );
PtCreateWidget( PtButton, window, i, args );
data = PtGridLayoutDataDflts;
data.h_span = 2;
data.flags = Pt_H_ALIGN_FILL;
i = 0;
i = 0;
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, 0 );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Name:", 0 );
PtCreateWidget( PtLabel, ctnr, i, args );
i = 0;
data = PtGridLayoutDataDflts;
i = 0;
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, 0 );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Location:", 0 );
PtCreateWidget( PtLabel, ctnr, i, args );
i = 0;
data = PtGridLayoutDataDflts;
data.flags = Pt_H_GRAB | Pt_H_ALIGN_FILL;
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, 0 );
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "North America", 0 );
PtCreateWidget( PtText, ctnr, i, args );
i = 0;
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_FILL;
data.h_span = 3;
data.hint.h = 6;
PtSetArg( &args[i++], Pt_ARG_SEP_FLAGS, Pt_SEP_HORIZONTAL, 0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtCreateWidget( PtSeparator, window, i, args );
i = 0;
data = PtGridLayoutDataDflts;
data.flags = Pt_H_ALIGN_END;
data.h_span = 3;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "Save", 0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout );
PtCreateWidget( PtButton, window, i, args );
PtRealizeWidget( window );
PtMainLoop();
return 0;
}
Using hints
Let’s take a look at an interesting way of using hints with the grid
layout. We will use a “draggable” separator to obtain the user’s input
and adjust the width hint of one of the widgets in the layout.
This is what we’ll do:
#define MARGIN_W 2
#define SPACING_W 2
#define SEP_WIDTH 5
/****************************************************************
DRAG_CB() ***/
int
drag_cb( PtWidget_t *widget, void *data, PtCallbackInfo_t *cbinfo)
{
PtSeparatorCallback_t *cb = (PtSeparatorCallback_t*)cbinfo->cbdata;
PtWidget_t *b1 = (PtWidget_t*)data;
short pos_x = cb->rect.ul.x;
PtGridLayoutData_t *_grid_data, grid_data;
const PhRect_t *p_canvas = PtGetCanvas( widget->parent
);
short parent_width = p_canvas->lr.x - p_canvas->ul.x&
#SPACE
+ 1;
return Pt_CONTINUE;
}
/*******************************************************************
MAIN() ***/
int
main( int argc, char *argv[] )
{
int i;
PtWidget_t *window, *sep, *b1, *b2, *b3;
PtArg_t args[20];
PhDim_t dim;
PtGridLayoutData_t data;
info.n_cols = 4;
info.margin.ul.x = info.margin.lr.x = MARGIN_W;
info.h_spacing = SPACING_W;
i = 0;
PtSetArg( &args[i++], Pt_ARG_WINDOW_TITLE, "example: Draggable
Separator", 0 );
PtSetArg( &args[i++], Pt_ARG_HEIGHT, 100, 0 );
PtSetArg( &args[i++], Pt_ARG_WIDTH, 400, 0 );
PtSetArg( &args[i++], Pt_ARG_CONTAINER_FLAGS, Pt_AUTO_EXTENT,
Pt_AUTO_EXTENT );
PtSetArg( &args[i++], Pt_ARG_LAYOUT, PtGridLayout, &info
);
data = PtGridLayoutDataDflts;
data.flags = Pt_V_ALIGN_FILL | Pt_H_ALIGN_FILL;
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "B1", 0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout
);
b1 = PtCreateWidget( PtButton, NULL, i, args );
data = PtGridLayoutDataDflts;
data.hint.w = SEP_WIDTH;
data.flags = Pt_V_ALIGN_FILL;
i = 0;
PtSetArg( &args[i++], Pt_ARG_SEP_ORIENTATION, Pt_VERTICAL,
0 );
PtSetArg( &args[i++], Pt_ARG_SEP_TYPE, Pt_DOUBLE_LINE,
0 );
PtSetArg( &args[i++], Pt_ARG_SEP_FLAGS, Pt_SEP_DRAGGABLE, Pt_SEP_DRAGGABLE&
#SPACE
);
PtSetArg( &args[i++], Pt_ARG_CURSOR_TYPE, Ph_CURSOR_DRAG_HORIZONTAL,
0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout
);
sep = PtCreateWidget( PtSeparator, NULL, i, args );
PtAddCallback( sep, Pt_CB_SEP_DRAG, drag_cb, b1 );
i = 0;
data = PtGridLayoutDataDflts;
data.flags = Pt_V_ALIGN_FILL | Pt_H_ALIGN_FILL | Pt_GRAB_BOTH;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "B2", 0 );
PtSetArg( &args[i++], Pt_ARG_LAYOUT_DATA, &data, PtGridLayout
);
b2 = PtCreateWidget( PtButton, NULL, i, args );
data = PtGridLayoutDataDflts;
data.flags = Pt_V_ALIGN_FILL | Pt_H_ALIGN_FILL | Pt_GRAB_BOTH;
i = 0;
PtSetArg( &args[i++], Pt_ARG_TEXT_STRING, "B3", 0 );
/* Column 2 to 4:
* separator width + spacing + b2 width + spacing + b3 width
+ right margin
*/
col2to4_width = SEP_WIDTH + SPACING_W;
PtRealizeWidget( window );
PtMainLoop( );
return 0;
}
PhRect_t old_size
A PhRect_t structure (see the Photon Library Reference) that
defines the former size of the container.
PhRect_t new_size
A PhRect_t structure that defines the new size.
In this chapter. . .
Using the Build menu 297
Generating application code 300
How application files are organized 306
Editing source 310
Compiling and linking 313
Customizing the build process 315
Running the application 316
Debugging 317
Managing targets 318
The Build menu 319
Including non-PhAB files in your application 320
Making a DLL out of a PhAB application 322
By doing all this, PhAB lets you get on with the job of writing the
code that implements your application’s main functionality.
The way you build PhAB projects depends on whether you’re running
PhAB standalone or from the IDE. This chapter describes building
and generating code using PhAB standalone, and indicates where
there are differences when using the IDE. For more information on
building and generating code in the IDE, see the “Building Projects”
section in the Developing Programs chapter of the IDE User’s Guide.
For most code generation, you can use the Build menu. However, you
can also generate some C and C++ stub files on the spot when using
various dialogs to develop your application; use the icons located next
to function or filename fields:
This means you’re free to edit a callback function while still in the
process of attaching it to the widget. You don’t have to go into the
Build menu, generate code from there, and then come back to write
the function.
• Select Build→Build.
When you build your application, PhAB creates an executable that
you can run on the target system.
If you haven’t already added targets to your application, the Select
New Platform dialog is displayed:
This dialog lets you select a target platform. For example, if you
plan to run your application on an SH processor, you should select
SH (Little Endian).
You can add more targets later using the Select New Platform dialog.
To open this dialog, select Build→Targets, then click Add target.
See Managing targets below.
Select your target and click Done.
• Generate the application by selecting Build→Generate UI. PhAB
saves your application and then generates the required files. See
Generating application code.
• PhAB opens the Make Application dialog and compiles and links
your application, generating an executable that can be run on your
target system. PhAB runs make in all of the selected target
directories. The make utility links and compiles the source files;
PhAB then runs binders to build the widget files into your
executable.
If any errors or warnings are detected during the make process and
it can determine which source file contains errors, PhAB enables
the Edit button.
To edit the first file that contains errors, click Edit. After fixing the
problems, click Restart to run make again.
To stop make at any time, click Cancel.
When you use PhAB from the IDE, PhAB regenerates the application
code every time you save the project.
If you plan to use a global header in your application, you should set
up the header before generating any code. For more information, see
“Setting project properties” in the Working with Applications chapter,
and “Global header file” in the Working with Code chapter.
You can modify any other files that PhAB generates, with few
conditions. These conditions are described in the following sections.
Here are the files that PhAB generates:
appname/Makefile
Used to compile and link the application
appname/common.mk
Contains traditional makefile options, such as
compiler options and link options.
src/abWfiles
Contains a list of PhAB modules that are part of
your application.
src/abdefine.h
Contains all the PhAB-generated manifests. PhAB
includes this header in all C files.
src/abevents.h
Contains all the application’s callbacks
src/abimport.h
The extern reference header that’s included in all
C files. See “Function prototypes”, below.
src/ablinks.h
Contains all the application’s module definitions
src/abwidgets.h
Contains all the PhAB data references
src/indHfiles
Contains additional header files not contained in
abHfiles.
src/indLfiles
Contains additional options for the linker.
src/indSfiles
Contains additional source files not contained in
abSfiles.
Version control
Here are the files you need to save if you’re using version-control
software (PhAB can generate some of them, but it’s a good idea to
save them all):
src/*.{c,cc,cpp,C,h}
Source files and headers.
src/*files, src/Usemsg
Files that list non-PhAB source files. Be sure to save
the non-PhAB source, too.
Makefile, common.mk
The make files.
target_directory/Makefile, target_directory/*/Makefile
The target files and their content.
application_name.ldb
Your application’s language database. Save any
translation files as well.
You’ll need to keep a matched set of all the files that PhAB generates;
save the same version of the abapp.dfn, src/ab*, and
wgt/*.wgt? files.
Function prototypes
PhAB generates function prototypes used by the compiler to check
that your functions are called correctly. These prototypes are placed
in abimport.h and optionally in proto.h. Here’s how these files
compare:
proto.h abimport.h
Generated by parsing your Generated by looking at your
source code. application’s settings.
Prototypes for all functions are Only prototypes known to
generated. PhAB (callbacks, setup
functions, pointer-to-function
resources) are generated.
You can have problems with Prototypes don’t depend on the
preprocessor directives (see source code.
“Potential problems with
generating proto.h”), unusual
C constructs, syntax errors, and
C++ code.
Doesn’t work with C++. Contains the appropriate
#ifdefs and extern "C"
declarations required by C++.
Prototypes match what the Prototypes match what the
functions look like. functions are supposed to look
like—if the source code is
different, the compiler can
detect it.
appl
Platforms
src wgt reports
At the top of the application’s directory structure are the wgt and src
directories. The wgt directory contains the application’s widget files
(files with extension .wgt* that PhAB uses to store information
about your modules that are part of your application). The src
directory contains header and source files needed to compile your
application. At the same level as src and wgt are the platform
directories (e.g. x86, mips, and arm). Their structure is the same as
the structure generated by the addvariant command. The reports
directory contains the files created when you choose the Generate
Report command from the Project menu.
This directory structure is called the “Eclipse PhAB project”
structure, because it is allows you to create an application in PhAB
and then load it in Eclipse as an “Eclipse PhAB project”. Conversely,
an application created in Eclipse can be loaded in PhAB.
Multiplatform applications
Here’s what each directory contains for a multiplatform application:
appl/src/platforms
These directories contain the Makefile, object files,
and executables for the chosen platforms. The
Makefile in the src directory runs those in the
platform directories.
CAUTION: Always use PhAB to edit the module files in the wgt
! directory. Don’t try to edit these binary files with another editor.
Never modify any files prefixed by ab.
Single-platform applications
Here’s what each directory contains for a single-platform application:
CAUTION: Always use PhAB to edit the module files in the wgt
! directory. Don’t try to edit these binary files with another editor.
Never modify any files prefixed by ab.
Converting to Eclipse
If you have a single-platform application built with an earlier version
of Photon, it is converted to “Eclipse Project” format when you load it
for the first time. When converting an application, PhAB moves any
existing Makefile to src/default/Makefile.old.
Editing source
You can edit source files from an external editor, which you can
configure in PhAB. If you’re using PhAB from the IDE, you can use
an editor in the IDE to work with source files as well.
You can open source files for editing within PhAB from the Browse
Files palette. If the Browse Files palette isn’t visible, select
Window→Show Project. The Browse Files palette looks like this:
1 Click Create to open the Create File dialog, then type the name
of the new file.
2 If you wish to create the file using a template (for a header file
or widget callback), select a format from the Template
combobox.
The Filter line at the bottom of the palette allows you to filter the list
of the files the Browse Files palette displays. For instance *.[ch]
displays subdirectories and filenames ending in .c and .h.
The default is shared libraries. To specify how libraries are linked, use
the -B option in the Link Libraries field of the Build and Debug
Options dialog. For example, consider the following Link Libraries
line: -Bstatic -lphexlib -Bdynamic -lmylib -Bstatic.
This links against libphexlib.a, mylib.so, and since the default
libraries are considered being after the end, libAp.a and libph.a.
You can also specify a list of library callback functions when you start
PhAB. For more information, see appbuilder in the Utilities
Reference.
Running make
Once you’ve chosen the library type, you’re ready to compile and link.
The first time you generate your application, PhAB creates Makefile
and common.mk files in the project directory (plus a Makefile for
each platform selected for multiplatform development) so you can
make the application. Subsequent generations don’t modify the file
directly; instead, they update external files referenced in the
common.mk.
Once the common.mk is generated you can modify it, though you
should only do so when there’s no way to set options from within
PhAB.
By default, the Makefile is compatible with the installed make
command. You can convert the file to a format that suits the make
command you prefer—just ensure the external file reference method
is still compatible.
For more information, see “Including non-PhAB files in your
application,” later in this chapter.
To make your application:
Any changes you make to Build Preferences settings are saved with
the application itself rather than as a global preference.
1 Select Run from the Build menu. The following dialog appears:
4 Click OK.
If the target you chose to build your application for is not the same as
the one you run PhAB on, you’ll have to transfer your application’s
executable to your target in order to run it.
Debugging
You can run your application with a debugger, which can be handy if
your application crashes or behaves incorrectly.
When running PhAB from the IDE, you use the debug features in the
IDE. For more information, see the Debugging Programs chapter in
the IDE User’s Guide.
To switch between the debugger and the application, use the Window
Manager’s taskbar.
Managing targets
You can add or delete target platforms using the Manage Targets
dialog. To open this dialog, select Build→Targets.
To add a target, click Add target. Note that if the target is already
added to your application, the Makefile in the target directory is
overwritten.
* For these commands, “all your selected targets” means the building
process will take place only for selected targets. To change the
currently selected targets simply choose Targets from the Build menu
and change the selection in the target list. You can also select targets
from the Build and Debug Options tab of the Project Properties
dialog.
Multiplatform applications
PhAB generates empty lists in the following files in the src directory,
and you can edit them:
• Header files and source files are usually in the parent directory,
src, so their names start with ../ .
Single-platform applications
A single-platform application from an earlier version of Photon
doesn’t have the indHfiles, indOfiles, and indSfiles files.
Instead, you’ll find MYHDR, MYOBJ, and MYSRC in your
Makefile, and you can specify filenames there.
Adding libraries
If your application uses a library that isn’t included by default in the
Makefile, you can add it with the Link Libraries line on the Build
and Debug Options tab of the Project Properties dialog. Open this
dialog by selecting Project→Properties.
For instance, if you want to link against the socket library, put the
following in the Link Libraries line:
-l socket.
You can also add it by editing the LDFLAGS variable in common.mk:
Before calling any PhAB code, the initialization function must call
ApAddContext(), like this:
This structure has the same name in every PhAB application or DLL,
so you must link your DLL with the -Bsymbolic option mentioned
above.
You can call ApAddContext() more than once, but you need to keep
track of how many times you called it.
ApAddContext() returns zero on success, or -1 on failure. Don’t call
any Ap* functions if ApAddContext() fails.
AbContext holds, among other things, the location of the executable or
DLL that it’s contained in, and the language translation files currently
loaded for that file. At any time, one of the registered contexts may be
“current”; a few libAp functions implicitly refer to the current
context, and some require a current context when you call them. At
program startup, the program’s context is made current. You can
unset it or change it to a different context by calling ApSetContext():
This makes the given context current, and returns the previous current
context. Both can be NULL; but if you pass a non-NULL pointer, it
must point to a registered context.
• destroy any PhAB widgets that “belong” to your DLL; this means
you have to destroy any widgets:
- created by PhAB link callbacks defined in the DLL
- ApCreateModule() using a module in the DLL
- that are using any of the widget databases that must be closed
In this chapter. . .
Variables and manifests 327
Global header file 331
Function names and filenames 333
Initialization function 334
Module setup functions 337
Code-callback functions 339
Geometry data types 341
Timers 342
Initializing menus 344
Delaying and forcing updates to the display 352
• ABN_done
• ABW_done
A widget’s global variable and manifest are valid only after the widget
has been created, and before it has been destroyed.
int
mycallback( PtWidget_t *widget, ... )
return( Pt_CONTINUE );
}
int
mycallback( PtWidget_t *widget, ... )
{
PtSetResource( ABW_done, Pt_ARG_FILL_COLOR, Pg_RED, 0 );
return( Pt_CONTINUE );
}
Remember that the global variable and the manifest are valid only
after the widget has been created and before it has been destroyed.
If you have two instances of the window on the screen at the same
time and the user clicks on the Search button, how can you get the
value in the Name text widget? Since two instances of the window
exist, two instances of the text widget exist. ABW_name_txt points to
the last instance of the text widget that was created.
The solution lies in the fact that ABN_name_txt can be used to refer to
both instances of name_txt, provided you have the widget pointer to
the window that contains the desired text widget. This is done using
the ApGetWidgetPtr() function:
Where do you get window_wgt? In the above case, you’d have a code
callback on the Search button. The first parameter passed to that code
callback is the widget pointer to the Search button. You can use
ApGetInstance() to get a pointer to the window that contains the
Search button.
So the callback would become:
return( Pt_CONTINUE );
}
• ABM_internal_link_name—where internal_link_name is a
pointer to the module’s internal definition.
For more information about using internal links, see the Accessing
PhAB Modules from Code chapter.
#include <Pt.h>
#ifdef DEFINE_GLOBALS
#define GLOBAL
#define INIT(x) = x
#else
#endif
/* global variables */
GLOBAL int variable1 INIT(1);
int variable1 = 1;
#define DEFINE_GLOBALS
Including this line ensures that the global variables are defined in this
code file and used as extern declarations in all other code files.
In the Makefile, make the code files dependent on the header file.
That way, if you make any changes to the header, all the code will be
recompiled when you make your application.
function_name
Create a C stub file called function_name.c
function_name@filename.ext
Create a stub function and put it in filename.ext. This file will
include the headers and function structure required to compile
in the Photon environment.
If this file already exists, the stub function is added to it. You
can use this technique to reduce the number of code files for
your application. You can place any number of functions in the
same file. We recommend you put all functions related to a
module in the same file.
function_name.ext
Short form for function_name@function_name.ext
class::function_name@filename.cc
Generate a stub C++ static member function, but no prototype.
class::function_name@
Don’t create a stub function or a prototype. Instead, invoke a
C++ static class member function. Prototypes aren’t generated
for class member functions; your application must have the
necessary declarations in its global header file.
function_name@
Generate a prototype for a C function, but not a stub. This is
useful if you’re using a library of C functions.
::function_name@
Generate a prototype for a C++ function, but not a stub. This is
useful if you’re using a library of C++ functions.
You can use C and C++ in the same PhAB application. See “What
PhAB generates” in the Generating, Compiling, and Running Code
chapter.
Initialization function
PhAB lets you define an application-level initialization function. The
PhAB API calls this function once at startup, before any windows or
other widgets are created. For information on setting up this function,
see “Startup Windows tab” in the Working with Applications chapter.
The initialization function includes the standard argc and argv
arguments so that your application can parse command-line options if
needed (see below). You can also use this function to open any widget
databases (see the Accessing PhAB Modules from Code chapter) or
other application-specific data files.
Here’s a sample initialization function generated by PhAB:
/* Y o u r D e s c r i p t i o n */
/* AppBuilder Photon Code Lib */
/* Version 2.01A */
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "abimport.h"
#include "proto.h"
int
init( int argc, char *argv[] )
return( Pt_CONTINUE );
fullpath fullpath
relative_path /dev/relative_path
-x position[%][r]
The x coordinate of the upper-left corner of the
window, in pixels, or as a percentage of screen
width if % is specified. If r is specified, the
coordinate is relative to the current console.
-y position[%][r]
The y coordinate of the upper-left corner of the
window, in pixels, or as a percentage of screen
height if % is specified. If r is specified, the
coordinate is relative to the current console.
You can suppress the options for the application’s size and
position—see “Command-line options” in the Working with
Applications chapter. You can also define additional options.
Edit the application’s usage message, which you’ll find in the Usemsg
file in your application’s src directory, to include any additional
options. For details about the usage message syntax, see usemsg in
the QNX Neutrino Utilities Reference.
• include a case branch for each option, but do nothing in it. You
could also include a default that prints a message if an invalid
option is given.
or
• don’t include case branches for them. If you do this, you won’t be
able to have a default branch.
The PhAB library also looks at the ApOptions array to take into
account the options you’ve added. For example, for the above code
the library recognizes that -px100 specifies the X position (along
with -p), while -ax100 doesn’t.
return( Pt_CONTINUE );
}
where:
ABR_POST_REALIZE
This setup function is being called after the
module is displayed on the screen.
Code-callback functions
A code-callback function is generated if you specify a code-type link
callback, as described in “Code callbacks” in the Editing Resources
and Callbacks in PhAB chapter.
All code-type callback functions have three main arguments:
int
mycallback( PtWidget_t *widget,
ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
return( Pt_CONTINUE );
}
where:
The Photon libraries provide various functions that work with these
data types:
PhAreaToRect()
Convert an area into a rectangle
PhDeTranslateRect()
Detranslate a rectangle (subtract offset)
PhRectIntersect()
Find the intersection of two rectangles
PhRectToArea()
Convert a rectangle into an area
PhRectUnion()
Determine a bounding box for two rectangles
PhTranslateRect()
Translate a rectangle (add offset)
Timers
If you wish to schedule your own operations at particular time
intervals, or if you just want to implement a time-out or trigger an
event at a particular time, you may want to have a timer-based
callback function. There are several ways to do this, with varying
amounts of difficulty and accuracy:
The Photon libraries also include some low-level timer routines, but
you need to be careful using them:
Using PtTimer
The easiest way to implement a timer is to use a PtTimer widget. It
defines these resources:
Pt_ARG_TIMER_INITIAL
Initial expiration time.
Pt_ARG_TIMER_REPEAT
Optional repeat interval.
Pt_CB_TIMER_ACTIVATE
Expiration callbacks.
RtTimer* functions
The RtTimer* functions (described in the Photon Library Reference)
give more accurate timing than PtTimer, but still not hard realtime.
They’re cover functions for the POSIX functions that manipulate the
kernel timers:
RtTimerCreate()
Create a realtime timer
RtTimerDelete()
Delete a realtime timer
RtTimerGetTime()
Get the time remaining on a realtime timer
RtTimerSetTime()
Set the expiration time for a realtime timer
These functions are more accurate than PtTimer because the timer is
rearmed by the kernel, not by Photon. However, if Photon is busy
handling events, there could still be delays in processing the
expiration events.
Initializing menus
You may want to do various things to a menu before it’s displayed.
You can use the menu’s setup function to:
You can also use a function menu item to generate new items at
runtime.
The methods for doing these things are discussed in the sections that
follow.
AB_ITEM_DIM
to disable the item
AB_ITEM_NORMAL
to enable and unset the item
AB_ITEM_SET
to set a toggle item
For example, our Draw menu might have an item that’s either Group
or Split, depending on what objects the user chooses. We could
change the text of the draw_group item in the draw_menu with the
following code:
ApModifyItemText (&draw_menu, ABN_draw_group, "Split");
return (Pt_CONTINUE);
}
PtWidget_t *new_item;
The last thing we need to do is add the callback to the menu item’s
Pt_CB_ACTIVATE callback list, using the PtAddCallback() function:
int
add_shapes (PtWidget_t *widget, ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo)
{
PtArg_t args[2];
PtWidget_t *new_item;
return (Pt_CONTINUE);
}
Creating submenus
You can create submenus in the menu created for a menu function
item as follows:
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "abimport.h"
#include "proto.h"
#define DOT 3
#define BLOB 4
#define POLYGON 5
int
ShapeMenuCB( PtWidget_t *widget, void *client_data,
PtCallbackInfo_t *cbinfo )
{
int shape_chosen = (int) client_data;
switch (shape_chosen) {
return (Pt_CONTINUE);
}
int
add_shapes( PtWidget_t *widget, ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
PtArg_t args[3];
PtWidget_t *menu, *new_item;
menu = PtGetParentWidget();
PtSetParentWidget (menu);
return( Pt_CONTINUE );
}
Globally
The Photon libraries use a hold count to let you delay updating the
display for your entire application:
For more information about these functions, see the Photon Library
Reference.
PtStartFlux()
PtEndFlux() When the container’s flux count goes to zero, you
must explicitly damage the areas you want to repair.
PtContainerHold()
PtContainerRelease()
When the container’s flux count goes to zero, the
entire container is marked as damaged.
Forcing updates
You can call PtFlush() at any time to immediately update the
damaged areas of the display. PtFlush() ignores the hold count and
doesn’t change it.
If a container is in flux, and you modify it or its children, the Photon
libraries don’t mark the widgets as damaged, so PtFlush() doesn’t
repair them.
In this chapter. . .
Argument lists 357
Setting resources 359
Getting resources 366
Application-level resources 376
This chapter describes how you can set and get the values of a
widget’s resources inside your application.
Although you can set the initial values of a widget’s resources in
PhAB, you’ll probably need to access them from your code. For
example:
• when a dialog appears, you may need to initialize some of the data
it displays by setting resources beforehand
• when the user types a value in a PtText widget, you may need the
value in your program, so you’ll have to get resources.
There are two steps involved in specifying or retrieving more than one
resource value:
Argument lists
An argument list is an array of PtArg_t structures (see the Photon
Library Reference). Each of these elements identifies a widget
resource and a new value for the resource (or the address of a variable
that will be set to the resource’s current value).
You can use the PtSetArg() macro to initialize each element of the
argument list:
The first two arguments to PtSetArg() are the address of the argument
list element, and the name of the resource. The third and fourth
arguments vary, depending on the type of the resource, and on
whether a set or a get operation is being applied. When setting a
resource, the third argument is always used to hold a resource value or
a pointer to a resource’s value.
The fourth argument is used as either a size indicator or a mask,
depending on the type of the value being specified. The possible
resource types are given in the table below:
Type: Description:
Alloc An arbitrarily sized memory object
Array An array
Boolean A bit that’s either on or off
Color A color
Complex A resource that’s handled in a special way; see below.
Flag A value in which each bit has a different meaning
Function A pointer to a function
Image A pointer to a PhImage_t structure
Link A linked list
Pointer A pointer to an address that you specify
continued. . .
Type: Description:
Scalar A value that can be represented within a single long
String A null-terminated string
Struct A fixed-size data type, usually a structure, float, or
double
For information about the resources defined for each widget, see the
Photon Widget Reference.
Setting resources
Remember that there are two steps involved in setting more than one
resource value:
After initializing the argument list, you’ll actually set the resources.
When you call PtSetResources(), the widget copies the scalar value
into its own internal data structure.
String resources
Setting a string value is similar to setting a scalar value; you specify
the string as the third argument to the PtSetArg() macro. The fourth
argument is the number of bytes to copy; if it’s 0, strlen() is used to
determine the length of the string.
For example, to set the default text for the combo box, you could
specify a value for the Pt_ARG_TEXT_STRING resource in one
element of the argument list:
PtSetArg(&args[1], Pt_ARG_TEXT_STRING,
"Rectangle", 0);
When you call PtSetResources(), the widget copies the string into its
own internal data structure.
If you need to use international (non-ASCII) characters in a string, do
one of the following:
• Define the string in a widget database and use the language editor
to translate the string. See the International Language Support
chapter.
Alloc resources
Some resources are designed to store an allocated block of memory.
For example, every widget includes a Pt_ARG_USER_DATA
resource that you can use to store any data you want in the widget’s
internal memory. To set this resource, pass a pointer to the data as the
third argument to PtSetArg(). The fourth argument is the size of the
block of memory, in bytes:
my_struct user_data;
The widget copies the number of bytes given into its internal memory
when you call PtSetResources().
Image resources
Image resources are designed to store a PhImage_t structure. For
example, a PtLabel has a Pt_ARG_LABEL_IMAGE resource that
you can use to store an image. To set this resource, create and
initialize the PhImage_t structure, and pass a pointer to it as the third
argument to PtSetArg(). The fourth argument is 0:
PhImage_t *my_image;
The widget copies the image structure (but not any memory pointed to
by the PhImage_t members) into its internal memory when you call
PtSetResources().
Array resources
When setting an array value, the third argument to PtSetArg() is the
address of the array. The fourth argument is the number of elements
in the array.
For example, the following entry in the argument list can be used to
set up Pt_ARG_ITEMS, the list of choices for the combo box:
char *cbox_items[3] = {"Circle", "Rectangle", "Polygon"};
The widget copies the contents of the array into its own internal data
structure when you call PtSetResources().
Flag resources
When setting a flag, the third argument to PtSetArg() is a bit field
specifying the value of the bits to be set. The fourth argument is a bit
mask indicating which elements of the bit field should be used.
When you call PtSetResources(), the widget uses the bit mask to
determine which bits of its internal flag resource representation to
alter. It takes the bit values from the value specified.
Function resources
When setting a function resource, pass a pointer to the function as the
third argument to PtSetArg(). The fourth argument is ignored; set it to
0.
For example, to specify a drawing function for a PtRaw widget, set
the Pt_ARG_RAW_DRAW_F resource as follows:
PtSetArg( &args[0], Pt_ARG_RAW_DRAW_F,
&my_raw_draw_fn, 0);
When you call PtSetResources(), the widget copies the pointer into
the resource.
Pointer resources
When setting a pointer resource, the pointer must be given as the third
argument to PtSetArg(). The fourth argument is ignored and should be
set to 0.
When you call PtSetResources(), the widget simply does a shallow
copy of the pointer into the resource.
The widget copies the value of the pointer into its internal memory
when you call PtSetResources().
Link resources
When setting a Link, pass the address of an array of data as the third
argument to PtSetArg(). The fourth argument has some special
meanings:
Pt_LINK_INSERT
insert the first array element at the beginning of the linked
list
Pt_LINK_DELETE
remove the first list element that matches the first array
element
The widget copies the data into its internal memory when you call
PtSetResources().
Struct resources
When setting a struct resource, pass the address of the data as the
third argument to PtSetArg(). The fourth argument isn’t used and
should be set to 0.
The widget copies the data into its internal memory when you call
PtSetResources().
Boolean resources
When setting a Boolean value, you should specify the value as the
third argument to PtSetArg(), using 0 for false, and a nonzero value
for true. The fourth argument isn’t used, and should be set to 0.
For example, to set the protocol for a PtTerminal to ANSI, pass a
nonzero value as the third argument:
PtSetArg(&args[1], Pt_ARG_TERM_ANSI_PROTOCOL, 1, 0);
When you call PtSetResources(), the widget clears or sets one bit in
its own internal data structure depending on whether or not the value
is zero.
Calling PtSetResources()
Once you’ve set up the argument list, you’re ready to set the
resources. Remember that PtSetArg() doesn’t set the resources; it just
sets up the argument list.
You can use PtSetResources() to set the new values for resources:
The arguments to this function are a pointer to the widget, the number
of entries in the argument list, and the argument list itself.
You can also set resources by passing an argument list to
PtCreateWidget(). The rules for specifying values in argument list
elements are the same. For more information, see “Creating widgets”
in the Managing Widgets in Application Code chapter.
For example, you could set the resources of a combo box, using the
argument list created above. Call PtSetResources() as follows:
It takes just one function call, and there’s no need for an args array.
Getting resources
There are two steps involved in retrieving more than one resource
value:
• You don’t need to worry about the type of the value (short versus
long).
• You have fewer local variables and don’t use pointers to them,
which makes your code easier to read and helps the compiler
generate better code.
The pointer method may be less confusing if you’re getting the values
of several resources at once; you’ll have named pointers to the values
instead of having to remember which element in the argument list
corresponds to which resource.
continued. . .
Using pointers
When using the pointer method to get a scalar, array, or flag resource,
the widget always gives a pointer to an internal widget data structure.
In the argument list element you set up using PtSetArg(), you must
provide the address of a variable to which the internal data pointer can
be assigned.
The fourth argument isn’t used for most types of resources. For
arrays, it’s the address of a pointer that on return from
PtGetResources() points to the number of entries.
For example, to obtain the contents of the Pt_ARG_FLAGS resource
(which is a long) for a widget, you must pass the address of a pointer
to a long:
If you wish to retrieve the value of a given resource and then modify
that value:
You can use the value obtained to set the value of another resource of
this or any other widget, as long as you don’t change the original
value.
For example, you can use the following code to obtain
Pt_ARG_TEXT_STRING, the text string displayed in the label widget
named label:
char *str;
PtArg_t args[1];
You can then assign this text string to another label named label2:
PtArg_t arg;
int bool_value;
if ( bool_value ) {
Calling PtGetResources()
Use PtGetResources() to obtain the values of each of the resources
identified in an argument list:
The arguments to this function are the identifier for the widget, the
number of entries in the argument list, and the argument list itself.
PtGetResources() returns 0 on success, or -1 if an error occurs. A
return code of -1 might indicate that you’ve tried to get the value of a
resource that isn’t defined for the widget.
Application-level resources
Applications have callback resources that you can set and get, just
like widgets, except the resources apply to the application as a whole
instead of individual widget instances. These resources apply to
applications:
• Pt_CB_APP_EXIT
• Pt_CB_APP_WCLASS_CREATED
• Pt_CB_FILTER
• Pt_CB_RAW
• Pt_CB_HOTKEY
• PtAppAddCallback()
• PtAppGetResource()
• PtAppGetResources()
• PtAppRemoveCallback()
• PtAppSetResource()
• PtAppSetResources()
Setting resources
You can set application-level resources using these functions:
• PtAppAddCallback()
• PtAppSetResource()
• PtAppSetResources()
Removing callbacks
You can remove a callback using PtAppRemoveCallback(). It takes
the same arguments as PtAppAddCallback(). For example, to remove
the callback added in the examples above:
PtAppRemoveCallback( Pt_CB_APP_EXIT, exit_cb, NULL );
Getting callbacks
You can retrieve a pointer to an application callback to examine it.
You can use PtAppGetResource() to get a single callback, or
PtAppGetResources() to get one or more.
For example, to retrieve a pointer to the application exit callback
added in the previous example, you would use:
PtAppCallback_t *my_exit_callback;
PtAppGetResource(Pt_CB_APP_EXIT, &my_exit_callback, 0 );
In this chapter. . .
Creating widgets 381
Ordering widgets 382
Callbacks 384
Event handlers 389
Widget styles 393
Creating widgets
Creating a widget in your application code is a bit more work than
creating it in PhAB. That’s because PhAB looks after a lot of the
physical attributes for you, including size, location, and so on. If you
create the widget in your code, you’ll have to set these resources
yourself.
To create a widget in your code, call PtCreateWidget(). The syntax is
as follows:
PtWidget_t *PtCreateWidget(
PtWidgetClassRef_t *class,
PtWidget_t *parent,
unsigned n_args,
PtArg_t *args );
You can specify the default parent (used if the parent argument to
PtCreateWidget() is Pt_DEFAULT_PARENT) by calling
Ordering widgets
The order in which widgets are given focus depends on the order in
which they were created or on the widget order specified in PhAB
(see “Ordering widgets” in the Creating Widgets in PhAB chapter).
The backmost widget is the first in the tab order; the frontmost widget
is the last.
If you’re creating widgets programmatically, you can creating them in
the order in which you want them to get focus, or you can use these
functions to change the order:
PtWidgetInsert()
Insert a widget in the widget family hierarchy
PtWidgetToBack()
Move a widget behind all its brothers
PtWidgetToFront()
Move a widget in front of all its brothers
PtFindFocusChild()
Find the closest focusable child widget
PtFindGuardian()
Find the widget responsible for another widget’s
actions
PtWidgetBrotherBehind()
Get the brother behind a widget
PtWidgetBrotherInFront()
Get the brother in front of a widget
PtWidgetChildBack()
Get the child that’s farthest back in a container
PtWidgetChildFront()
Get the child at the very front of a container
PtWidgetFamily()
Traverse the widget hierarchy from back to front
PtWidgetParent()
Get a widget’s parent
PtWidgetTreeTraverse()
Walk the widget family hierarchy from front to
back
Callbacks
You can add and remove callbacks in your code as well as from PhAB
— just watch for differences between the two types!
Adding callbacks
An application registers callbacks by manipulating the widget’s
callback resources. The Photon widget classes employ a naming
convention for these resources — they all begin with Pt_CB_.
Callbacks can be added to the callback list kept by these resources
using PtAddCallbacks() to add several callback functions to the list or
{
PtWidget_t *button;
int push_button_cb( PtWidget_t *, void *,
PtCallbackInfo_t *);
PtCallback_t callbacks[] = { {push_button_cb, NULL} };
...
{
PtWidget_t *button;
Callback invocation
When called, the callback function is invoked with the following
parameters:
PtWidget_t *widget
The widget that caused the callback function to be called, i.e.
the one on which the action took place.
void *client_data
Application-specific data that was associated with the callback
when it was registered with the widget.
The client data that’s passed to a callback you add from your code
isn’t the same as the apinfo data passed to a PhAB callback.
PtCallbackInfo_t *call_data
A pointer to a PtCallbackInfo_t structure (see the Photon
Widget Reference) that holds data specific to this invocation of
the callback. It relates to the reason the callback was called and
may have data specific to the callback’s behavior.
The PtCallbackInfo_t structure is defined as:
Removing callbacks
You can remove one or more callbacks from a callback list associated
with a widget resource using the PtRemoveCallbacks() and
PtRemoveCallback() functions.
or this:
int push_button_cb( PtWidget_t *, void *,
PtCallbackInfo_t *);
PtRemoveCallback(button, Pt_CB_ACTIVATE, push_button_cb,
Both the callback function pointer and the client data pointer are
important when removing callbacks. Only the first element of the
callback list that has both the same callback function and the same
client data pointer will be removed from the callback list.
Examining callbacks
You can examine the callback list by getting the value of the
appropriate callback list resource. The type of value you get from a
callback list resource is different from the value used to set the
resource. Although this resource is set with an array of callback
records, the value obtained by getting the resource is a pointer to a list
of callback records. The type of the list is PtCallbackList_t. Each
element of the list contains a cb member (i.e. the callback record) and
a next pointer (which points to the next element of the list).
The following example shows how you can traverse through the
Pt_CB_ACTIVATE callback list for widget to find all instances of a
particular callback function, cb:
...
PtCallbackList_t *cl;
Event handlers
You can add and remove event handlers (raw and filter callbacks) in
your application code as well as in PhAB — however, there are some
differences between the two types.
For a description of raw and filter callbacks and how they’re used, see
“Event handlers — raw and filter callbacks” in the Events chapter.
For information on adding event handlers in PhAB, see “Event
handlers — raw and filter callbacks” in the Editing Resources and
Callbacks in PhAB chapter.
• Pt_CB_FILTER
• Pt_CB_RAW
For more information about these callback resources, see the Photon
Widget Reference.
The set operation requires an array of event handler records of type
PtRawCallback_t. These are similar to the callback records
mentioned above, having event_mask, event_f , and data fields.
The event mask is a mask of Photon event types (see PhEvent_t in
the Photon Library Reference) indicating which events will cause the
callback function to be invoked. The event_f and data members are
the event handler function and client data, respectively.
This looks for an event handler with the same signature — i.e. the
same event_mask, data and event_f — in the widget and removes one
if it’s found.
The parameters to PtRemoveEventHandlers() and
PtRemoveFilterCallbacks() are:
The client data passed to this event handler isn’t the same as the
apinfo data passed to an event handler added through PhAB.
Event handlers return an integer value that the event handler must use
to indicate whether or not further processing should be performed on
the event. If the event handler returns the value Pt_END, this indicates
that no further processing is to be performed on the Photon event, and
the event is consumed.
The event member of the info parameter contains a pointer to the
event that caused the event handler to be invoked. You should check
the type member of this event to determine how to deal with the event.
It will be one of the event types specified in the event_mask given
when the event handler was added to the widget.
To retrieve the data associated with the particular event, call the
PhGetData() with the pointer to the event as a parameter. This will
Widget styles
Widget class styles let you customize or modify a widget’s
appearance, size, and behavior at runtime. They also let multiple
looks for a single type of widget exist at the same time. Essentially, a
widget class style is a collection of methods and data that define the
look and feel of instances of the widget class.
Each widget class has a default style, but you can add or modify an
arbitrary number of additional styles at any time. You can even
modify the default style for a class, changing the look and feel of any
instances of that class that are using the default style.
Each instance of a widget can reference a specific style provided by
its class. You can change the style that any widget is using whenever
you want.
Each style has a set of members, including a name for the style and
functions that replace or augment some of the widget class’s methods.
Methods are class-level functions that define how the widget
initializes itself, draws itself, calculates its extent, and so on. For more
information about methods, see the Building Custom Widgets guide.
The members of a style are identified by the following manifests:
Pt_STYLE_DRAW
The address of a function that’s called whenever any widget
that’s using this style needs to draw itself.
Pt_STYLE_EXTENT or Pt_STYLE_SIZING
The address of a function that whenever a widget that’s using
this style is moved, resized, or modified in some fashion that
may require the widget to move or resize (change in widget
data). This function is responsible for setting the widget’s
dimension to the appropriate values.
Pt_STYLE_ACTIVATE
The address of a function that’s called whenever a widget is
created that defaults to this style, and whenever a widget’s style
is changed from some other style to this one. This function is
the place to put manipulation of a widget’s control surfaces, the
addition of callbacks, or the setting of resources (to override the
widget’s defaults).
Pt_STYLE_CALC_BORDER
The address of a function that’s responsible for reporting how
much space is required to render the widget’s edge decorations
and margins.
Pt_STYLE_CALC_OPAQUE
The address of a function that’s responsible for calculating the
list of tiles that represents the opaque areas of a widget. This
list is used to determine what needs to be damaged below this
widget when it’s modified.
Pt_STYLE_DEACTIVATE
The address of a function that’s called whenever a widget using
this style is either being destroyed or is switching to a different
style.
Pt_STYLE_NAME
The name of the style.
Pt_STYLE_DATA
A pointer to an arbitrary data block for the style’s use.
PtAddClassStyle()
Add a style to a widget class
PtCreateClassStyle()
Create a class style
PtDupClassStyle()
Get a copy of a widget class style
PtFindClassStyle()
Find the style with a given name
PtGetStyleMember()
Get a member of a style
PtGetWidgetStyle()
Get the style that a widget is currently using
PtSetClassStyleMethods()
Set multiple members of a style from an array
PtSetStyleMember()
Set a member of a style
PtSetStyleMembers()
Set multiple members of a style from a variable-length
argument list
PtSetWidgetStyle()
Set the current style for a widget
You can also set the style for a widget instance by setting its
Pt_ARG_STYLE resource (see PtBasic in the Widget Reference).
Setting this resource has the same effect as calling PtSetWidgetStyle().
This example creates a style called blue and some buttons. Note that
your widgets can use a style before you’ve added the style to the class
or even before you’ve created the style. When you do create the style
and add it to the class, any widgets that use the style are updated
immediately.
#include <Pt.h>
PgSetFillColor( Pg_BLUE);
PgDrawRect( PtWidgetExtent (widget,NULL),
Pg_DRAW_FILL);
}
int main()
{
PhArea_t area = {{0,50},{100,100}};
PtArg_t argt[10];
PtStyleMethods_t meth;
PtCallback_t cb = {use_blue_style, NULL};
PtCallback_t cb2 = {attach_blue_style, NULL};
int unsigned n;
PtInit(NULL);
n = 0;
PtSetArg (&argt[n++], Pt_ARG_TEXT_STRING,
"Attach blue style", 0);
PtSetArg (&argt[n++], Pt_CB_ACTIVATE, &cb2, 1);
PtSetArg (&argt[n++], Pt_ARG_POS, &area.pos, 0);
area.pos.y = 85;
but = PtCreateWidget (PtButton, NULL, n, argt);
PtRealizeWidget (win);
PtMainLoop();
return EXIT_SUCCESS;
}
Photon hook
Photon provides a mechanism for you to allow a block of user code to
be pulled in and executed during the initialization of Photon
applications. This functionality is most frequently used to customize
widget styles, allowing you to change the appearance and behavior of
widgets without having to re-compile, re-link, or otherwise
reconstruct executables.
The Photon hook can be used for many other things besides widget
styles. For example, it can be used to log application usage
information, or for more complicated situations such as remote
control of an application.
Multi-hook
You can use the pt_multihook.so DLL and rename it as
PtHook.so to load one or several DLLs, pointed to by the
PHOTON_HOOK environment variable. If PHOTON_HOOK
points to a DLL, that DLL is loaded and its PtHook() function is
executed. If PHOTON_HOOK points to a directory, each DLL in it
is loaded and its PtHook() function executed.
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <dirent.h>
#include <photon/PtHook.h>
The function can return Pt_END to ensure the DLL is not unloaded by
PtInit(), or return Pt_CONTINUE to ensure DLL is unloaded.
Place the PtHook.so in the search path to change the button style for
all Photon applications. You can get the search path with getconf
_CS_LIBPATH.
#include <Pt.h>
PgSetFillColor( Pg_BLUE);
PgDrawRect( PtWidgetExtent (widget,NULL),
Pg_DRAW_FILL);
}
int
PtHook (void *data)
{
PtStyleMethods_t button_meth = { Pt_STYLE_DRAW, blue_draw };
PtWidgetClassStyle_t *button_style = PtFindClassStyle( PtButton, NULL );
button_draw = button_style->draw_f;
PtSetClassStyleMethods( button_style, 1, &button_meth );
return( Pt_END );
}
In this chapter. . .
What’s a control surface? 403
Control-surface API 405
Example 411
Limitations
There are a few limitations to control surfaces:
Regular surfaces
Let you define an event mask and callback function for the
control surface.
Action surfaces
Let you automatically bind a control surface to one of a
widget’s predefined actions.
While the pointer method is more direct and therefore quicker, it’s not
as safe as the ID method. To understand why, consider how control
surfaces are organized and stored in memory.
Unlike the widget hierarchy, which is implemented as a linked list,
control surfaces are stored as an array of surface structures
(PtSurface_t). The major reasons for storing them this way are:
Control-surface API
The functions listed below are described in the Photon Library
Reference.
PtCreateActionSurface()
Create a control surface within a widget, bound to a widget
action
PtCreateSurface()
Create a regular control surface within a widget
PtDestroyAllSurfaces()
Destroy all of a widget’s control surfaces
PtDestroySurface()
Destroy a control surface
PtDestroySurfaceById()
Destroy the control surface with a given ID
PtSurfaceActionId()
Get the action ID for a surface
The post argument that’s passed to the geometry function tells you
which case is in progress.
A surface may also calculate its geometry based on the geometry of
other surfaces. Using PtCalcSurface() or PtCalcSurfaceById(), you
can ensure that the surface you’re interested in has calculated its
geometry prior to examining it.
PtCalcSurfaceByAction()
Force all surfaces associated with an action to
calculate their geometry
PtCalcSurfaceById()
Force the control surface with a given ID to
calculate its geometry
PtSurfaceCalcBoundingBox(), PtSurfaceCalcBoundingBoxById()
Calculate the bounding box for a control surface
PtSurfaceExtent(), PtSurfaceExtentById()
Calculate the extent of a control surface
PtSurfaceRect(), PtSurfaceRectById()
Get the bounding box of a control surface
PtSurfaceTestPoint()
Test whether or not a point is inside a control
surface
stack after the last surface is drawn. Otherwise, you’ll get some
unexpected results.
The following functions damage control surfaces so they’ll be
redrawn:
PtDamageSurface(), PtDamageSurfaceById()
Mark a surface as damaged so that it will be redrawn
PtDamageSurfaceByAction()
Damage all surfaces that are associated with an action
PtCheckSurfaces()
Match an event with the control surfaces belonging to a widget
PtDisableSurface(), PtDisableSurfaceById()
Disable a control surface
PtDisableSurfaceByAction()
Disable all control surfaces associated with an action
PtEnableSurface(), PtEnableSurfaceById()
Enable a control surface
PtEnableSurfaceByAction()
Enable all control surfaces associated with an action
PtSurfaceIsDisabled()
Determine if a control surface is disabled
PtSurfaceIsEnabled()
Determine if a control surface is enabled
PtFindSurface()
Find the control surface with a given ID
PtFindSurfaceByAction()
Find the control surface associated with a given action
PtWidgetActiveSurface()
Get a widget’s currently active control surface
PtHideSurface(), PtHideSurfaceById()
Hide a control surface
PtHideSurfaceByAction()
Hide all control surfaces associated with an action
PtShowSurface(), PtShowSurfaceById()
Show a hidden control surface
PtShowSurfaceByAction()
Show all hidden control surfaces associated with an action
PtSurfaceIsHidden()
Determine if a control surface is hidden
PtSurfaceIsShown()
Determine if a control surface is shown
PtInsertSurface(), PtInsertSurfaceById()
Insert a control surface in front of or behind another
PtSurfaceBrotherBehind()
Get the control surface behind a given one
PtSurfaceBrotherInFront()
Get the control surface in front of a given one
PtSurfaceInBack()
Get the backmost control surface belonging to a widget
PtSurfaceInFront()
Get the frontmost control surface belonging to a widget
PtSurfaceToBack(), PtSurfaceToBackById()
Move a control surface behind all other control surfaces
belonging to a widget
PtSurfaceToFront(), PtSurfaceToFrontById()
Move a control surface in front of all other control surfaces
belonging to a widget
PtSurfaceAddData(), PtSurfaceAddDataById()
Add data to a control surface
PtSurfaceGetData(), PtSurfaceGetDataById()
Get data associated with a control surface
PtSurfaceRemoveData(), PtSurfaceRemoveDataById()
Remove data from a control surface
Example
Here’s a program that creates some control surfaces:
#include <Pt.h>
PhRect_t *extent;
PhRect_t *srect;
srect->ul = extent->ul;
srect->lr.x = (extent->ul.x + extent->lr.x) / 2;
srect->lr.y = (extent->ul.y + extent->lr.y) / 2;
}
}
PtRealizeWidget(window);
PtMainLoop();
return(EXIT_SUCCESS);
}
In this chapter. . .
Creating internal links 418
Using internal links in your code 419
Using widget databases 422
4 Fill in the fields in the Module Link Info section — see below.
To create the manifest name, PhAB takes the module’s name and adds
ABM_ as a prefix. So, for example, if you create an internal link to a
module named mydialog, PhAB creates the manifest
ABM_mydialog.
Internal-link functions
The manifest is used by the following PhAB API functions:
ApCreateModule()
Lets you manually create modules designed within PhAB.
A module created with this function behaves exactly as if it
were linked directly with a link callback. For example, if you
define a location and a setup function for the internal link, the
module will appear at that location and the setup function will
be called. Furthermore, widget callbacks, hotkeys, and so on
will become active.
ApModuleFunction()
Lets you change the setup function associated with an internal
link.
ApModuleLocation()
Lets you change the display location associated with an internal
link.
ApModuleParent()
Lets you change the parent of a window or dialog module
associated with an internal link. This function applies only to
internal links for window and dialog modules.
ApOpenDBase()
Lets you open the module associated with an internal link as a
widget database.
For more info on the above functions, see the Photon Library
Reference.
return( Pt_CONTINUE );
Creating a database
To create a widget database:
Preattaching callbacks
Besides being able to preset all of a widget’s resources in the database
module, you can also preattach its callbacks. When you create the
widget dynamically, any callbacks you attached will also be created.
By presetting the resources and callbacks of a database widget, you
can easily reduce the code required to dynamically create the widget
to a single line.
Preattached callbacks work only with modules and functions that are
part of your executable. If your application opens an external file as a
widget database, the PhAB library won’t be able to find the code to
attach to the callback.
Widget-database functions
PhAB provides several support functions to let you open a widget
database and copy its widgets into modules—you can copy the
widgets as often as needed. PhAB also provides convenience
functions to let you copy widgets between databases, create widgets,
delete widgets, and save widget databases.
ApOpenDBase()
ApCloseDBase()
These let you open and close a widget database.
To ensure that the database is always available, you
typically use ApOpenDBase() in the application’s
initialization function.
ApOpenDBaseFile()
ApSaveDBaseFile()
These let you open and save external module files
as databases within your application.
ApCreateDBWidget()
ApCreateDBWidgetFamily()
ApCreateWidget()
ApCreateWidgetFamily()
These create widgets from the widget database.
ApCreateWidget() and ApCreateDBWidget() create
a single widget only, regardless of the widget’s
class.
For a noncontainer-class widget,
ApCreateWidgetFamily() and
ApCreateDBWidgetFamily() create a single widget;
for a container-class widget, they create all the
widgets within the container.
These functions differ in the parent used for the
widgets:
• ApCreateDBWidget() and
ApCreateDBWidgetFamily() include a parent
argument; if this is NULL, the widget has no
parent.
• ApCreateWidget() and ApCreateWidgetFamily()
put the new widget(s) in the current parent. To
make sure the correct widget is the current
parent, call PtSetParentWidget() before calling
either of these functions.
Don’t use the manifests generated for the widget database’s picture
module. Instead, use the widget pointers returned by
ApCreateWidget() or ApCreateDBWidget().
ApCopyDBWidget()
Lets you copy a widget from one widget database
to another. Typically, you use this only when
you’re dynamically creating and saving widget
databases within your application.
ApDeleteDBWidget()
Deletes a widget from a widget database.
ApGetDBWidgetInfo()
Gets information about a widget in a widget
database, including its name, class, parent, and
level in the hierarchy.
ApGetImageRes()
Pull out image-resource data from a widget and use
this data to set resources of a widget already
displayed in your application. This function lets
you achieve very basic animation.
ApRemoveClass()
Remove a widget class. If you’ve loaded a DLL
that defines widget classes, you should remove
them before unloading the DLL. For more
information, see “Making a DLL out of a PhAB
application” in the Generating, Compiling, and
Running Code chapter.
For more info on widget database functions, see the Photon Library
Reference.
In this chapter. . .
Application design considerations 431
Generating a language database 438
Message databases 439
Language editor 440
Running your application 446
Distributing your application 448
3 The translated text strings are saved in a translation file, and are
shipped with your application.
• The translated button could become much larger. In this case, the
button may be so wide that it writes on top of other widgets in the
window. This would cause the application to look ugly or poorly
designed.
or
• The text could be truncated within the default size. In this case, the
translated text would be unreadable, and the user wouldn’t know
what the button does.
Justification
In addition to making text-based widgets wider to accommodate
translated text, you should give some thought to the justification of
text, based on the widget’s usage. For example, in a simple text entry
field, it’s quite common to place a label to the left side of the field. If
you make the label wider to allow for translation, the label itself
moves to the far left:
When the text is later translated, it’s either too short or too long, and
the box label looks lopsided. The simple solution is to make the box
title much wider than necessary, and set the horizontal alignment to be
centered.
There are probably many other cases similar to this but the important
point is to think about how the translated text will effect the look of
the application. A lot of aesthetics can be maintained simply by
making text-based widgets wider and setting an appropriate
justification.
Font height
The fonts for some languages, such as Japanese or Chinese, are only
readable at large point sizes. For these fonts, the minimum size may
be 14 points or even larger. If you’ve designed your entire application
using a 10-point Helvetica font, you’ll have lots of problems when all
your text-based widgets are stretched 4 or more pixels taller to
accommodate the larger fonts. If your application needs to be
translated to other languages, look into the font requirements before
you begin, and use this minimum font size in the default language
built into the application.
If you really want to use the smaller font sizes for your default
application text, you can borrow a tip from the previous section. You
can make the height of widget larger and set the vertical alignment to
center. However, this may not work well for text input fields, and you
should keep this consideration in mind.
Hard-coded strings
Another major area for consideration is with informational, warning,
error or any textual messages that are displayed in popup dialog
ApCloseMessageDB(textdb);
char *btns[3];
This sounds fine, except PhAB also requires that all instance names
be unique. This rule must be adhered to so that PhAB knows which
text string to replace at run time. Unfortunately, dreaming up
potentially hundreds of unique instance names that you don’t really
care about can be a lot of work. To simplify this task, PhAB lets you
specify a single @ character for the instance name, and PhAB appends
an internal sequence number to the end. This eliminates the need to
keep track of all constant text strings that require instance names just
for translation.
If you want to group translation text strings (say, by module), you can
give them all the same instance name, and PhAB will append a
sequence number to make the name unique. For example, if you
assign the name @base to several widgets, PhAB generates @base,
@base0, @base1, ... as instance names.
Bilingual applications
Sometimes it’s necessary to design an application to be bilingual.
This means two different languages are displayed in every text string.
While this can be done, it’s usually difficult for the user to read and
understand.
PhAB allows you to use another approach. You can create the
application in one language and provide the ability to flip to the other
language within application control. This is done via a PhAB API
function named ApSetTranslation(). This function (which is described
in the Photon Library Reference) changes the current translation file
for the application immediately, such that all future dialogs, windows,
and so on are drawn using the new translation file.
Any existing modules and widgets aren’t translated, only new ones. If
you want immediate feedback, you need to recreate the modules. This
is easy for dialogs, but more difficult for the base window; remember
that destroying the base window exits the application. One way to
translate the contents of the base window is to put them in a picture
module, which can be recreated.
Common strings
If you have several applications to translate, you can reduce the work
by sharing the common text strings and translating them separately.
To do this:
3 Once the database is created and translated, you can open it and
use the strings in other applications using ApLoadMessageDB()
and ApGetMessage().
3 Click Done.
The database has now been generated and is ready for use with the
PhAB Language Editor. The name of the database is app.ldb, where
app is the name of the executable file for the application (which is
typically the same as the name of the application, unless you’ve used
the Save As command to rename the application). The language
database is placed in the application’s directory (where the
abapp.dfn file is found).
Message databases
A message database is a file that contains textual messages. Each
message is identified by a tag name.
To load a message database, call ApLoadMessageDB(). This function
does the usual file search based on the ABLPATH environment
variable and the current language:
You can create message databases using the PhAB message database
utility phabmsg, located in the PhAB application directory. This
utility allows you to create new message databases, and edit existing
message databases.
Language editor
After the database has been generated, you can use PhAB’s Language
Editor to translate the default text strings to another language. The
Language Editor is designed to work both as a stand-alone application
that you can distribute with your application, or as an integrated part
of PhAB itself.
• /usr/photon/appbuilder/phablang
• /usr/photon/appbuilder/languages.def
The Language Selection dialog closes, and you should now see the
newly created translation file in the list of available translations.
The Text Translation Editor dialog appears. This editor displays all
the text strings available for translation in the current language
database.
1 Click on the text string you want to translate. The selected text
string is displayed in the text areas at the bottom of the window:
You can use the cut, copy, and paste buttons that are above the
Translation area when editing the translations.
2 Once you change the translated string, a green check mark and
red X appear above the Translation area.
Repeat the above steps for all the text strings you need to translate.
When you’re finished, click on the Save & Close button.
Hotkeys
One problem with translating an application is that the hotkey
assignments no longer match up if the translated string doesn’t
include the accelerator key value. For this reason, PhAB adds the
accelerator key strings to the language database too.
When translating the text string, the translator can also change the
accelerator key. If the key used in the hotkey isn’t a function key (i.e.
the key code is less than 0xF000), PhAB automatically changes the
hotkey to match the accelerator key.
For example, suppose your application has a button labeled Cancel.
You’d set the button’s Pt_ARG_ACCEL_KEY to be C, and arrange
for Alt-C to invoke Pt_CB_HOTKEY.
When you generate the language database, you’ll find that it includes
the button’s label and its accelerator key. If you translate the
application into French, the button’s label would become Annuler,
so the hotkey Alt-C is no longer appropriate. Just translate the button’s
Pt_ARG_ACCEL_KEY to be A, and the hotkey automatically
becomes Alt-A when you run the application in French.
Help resources
If you use the Photon Helpviewer for your application help and you
plan on providing multiple language help files for your application,
the translator can also translate the help topic paths to point to the
correct positions within the corresponding help files.
Translation functions
You can build a custom language editor if the default one doesn’t
meet your needs. You’ll find these functions (described in the Photon
Library Reference) useful:
AlClearTranslation()
Clear all the translations in a language or message
database
AlCloseDBase()
Close a language or message database
AlGetEntry() Get an entry from a language or message database
AlGetSize() Get the number of records in a language or message
database
AlOpenDBase()
Load a language or message database
AlReadTranslation()
Read a translation file into a database
AlSaveTranslation()
Save translations from a language or message
database
AlSetEntry() Set the translated string for a database entry
You can use these functions to create your own language editor, or to
convert a language database to a different file format (for example, so
you can send the file to a non-Photon or non-QNX system for
translation).
Language: Value:
Belgian French fr_BE
Canadian English en_CA
Canadian French fr_CA
continued. . .
Language: Value:
Danish da_DK
Dutch nl_NL
French fr_FR
German de_DE
Italian it_IT
Japanese ja_JP
Norwegian no_NO
Polish pl_PL
Portuguese pt_PT
Slovak sk_SK
Spanish es_ES
Swedish se_SE
Swiss French fr_CH
Swiss German de_CH
UK English en_GB
USA English en_US
This list is current at the time this document was written, but may
have since been updated. For the latest version, see the file:
/usr/photon/appbuilder/languages.def
$ export ABLANG=de_DE
$ myapplication
The application looks for the best match. For example, if the language
extension specified is fr_CA, the search is as follows:
The export command could be put in the user’s login profile so that
the application will run in each user’s preferred language.
The language database and the translation files that the customer
creates should be in:
In this chapter. . .
Referring to help topics 453
Connecting help to widgets 454
Accessing help from your code 456
For information about creating help files for use with the helpviewer,
see “Creating Help Files” in the helpviewer topic in the QNX
Neutrino Utilities Reference.
• topic path
Topic path
A topic path is a group of concatenated topic titles that are defined in
the current topic tree. For example, here’s the equivalent topic path to
the above URL:
/Photon microGUI/Programmer’s Guide/Window Management
level as the book set, and the third level as the book. A book may
contain further levels of chapters and sections.
Entries in a bookshelf or book set should not contain any HTML, only
.toc entries for the next level; help text should only be found in
books.
The topic root should start with a slash (/), and should be the
top of all topics for the window, usually taken from a TOC file
in the /usr/help/product directory. For example:
/Photon microGUI/User’s Guide
When the user clicks on the ? icon and selects the widget, the help
information is displayed in the Helpviewer.
If you get an error message about a bad link when you ask for help for
a widget, make sure that the topic path is correct.
1 Put the text you want displayed in the balloon into the widget’s
Help Topic (Pt_ARG_HELP_TOPIC) resource.
When the user clicks on the ? icon, and selects the widget, the help
information appears in a balloon.
• You may want the mouse pointer to change to the Help pointer
when a key is pressed.
To get the mouse pointer to change to the Help pointer, forward the
Ph_WM_HELP event to the window manager. The following code
would be in a callback attached to a PtButton widget labeled Help:
int
help_cb( PtWidget_t *widget, ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
PhWindowEvent_t winev;
return( Pt_CONTINUE );
}
PtHelpUrlRoot()
Set a URL root.
PtHelpTopicRoot()
Set a topic root.
PtHelpTopicTree()
Display help for the topic tree.
In this chapter. . .
Connections 462
Sending QNX messages 469
Receiving QNX messages 471
Photon pulses 478
Processing signals 484
Other I/O mechanisms 486
• messages
• pulses
• signals
On the other hand, here’s why raw Neutrino messages and/or pulses
might sometimes be better:
• process signals
• respond to pulses
Connections
The process of establishing a connection uses an object called a
connector. The connector is a name that the server creates and owns,
and the client attaches its connection to. The connector is used only
for establishing a connection.
The connector has a numeric ID and may also have a name associated
with it. Both the name and the ID are unique in their Photon session.
Here are a few examples of how the name can be used:
Naming conventions
You can define unique names for your connectors by following these
naming conventions:
• Names that don’t contain a slash are reserved for QNX Software
Systems.
Typical scenario
Here’s how you typically use connections:
You can pass user data with the connection. The server calls
PtConnectionServerSetUserData() to specify the data that the client
can retrieve by calling PtConnectionClientGetUserData(). Similarly,
the client calls PtConnectionClientSetUserData() to specify the data
that the server can retrieve by calling
PtConnectionServerGetUserData().
You can set up functions to handler errors; the server does this by
calling PtConnectionServerSetError(), and the client by calling
PtConnectionClientSetError().
The server can also use events to communicate with the client:
Local connections
It’s possible for a process to create a connection to itself. The behavior
of such a connection differs a little bit from a normal connection:
again, the call fails with errno set to EBUSY (that’s how the library
protects itself from infinite recursion).
The simplest way around this is to avoid sending notifications from
within a message handler — instead, the notification can be placed
in the reply.
Example
This application uses a connector to determine if there’s already
another instance of the application running. The program takes two
command-line options:
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "abimport.h"
#include "proto.h"
enum MyMsgType {
MY_MSGTYPE_EXIT, MY_MSGTYPE_OPEN_DOC, MY_MSGTYPE_TOFRONT
};
enum MyReplyType {
MY_REPTYPE_SUCCESS, MY_REPTYPE_BADMSG
};
struct MyMsg {
struct MyReply {
enum MyReplyType status;
};
reply.status = MY_REPTYPE_SUCCESS;
switch ( type ) {
case MY_MSGTYPE_EXIT :
PtConnectionReply( connection, sizeof(reply),
&reply );
PtExit( EXIT_SUCCESS );
break;
case MY_MSGTYPE_OPEN_DOC :
reply.status = OpenNewDocument( msg->docname );
break;
case MY_MSGTYPE_TOFRONT :
break;
default :
reply.status = MY_REPTYPE_BADMSG;
}
PtWindowToFront( ABW_base );
*reply_len = sizeof(reply);
return &reply;
}
{
static const PtConnectionMsgHandler_t
handlers = { 0, msghandler };
if ( PtConnectionAddMsgHandlers( connection,
&handlers, 1 ) != 0 ) {
fputs( "Unable to set up connection handler\n", stderr );
PtConnectionServerDestroy( connection );
}
}
case ’e’ :
msgtype = MY_MSGTYPE_EXIT;
break;
case ’f’ :
document = optarg;
}
if ( document )
if ( msgtype == MY_MSGTYPE_EXIT ) {
fputs(
"You can’t specify both the -e and -f options\n",
stderr );
PtExit( EXIT_FAILURE );
} else {
msgtype = MY_MSGTYPE_OPEN_DOC;
strncpy( msg.docname, document,
sizeof(msg.docname)-1 );
}
PtConnectionClient_t *clnt;
if ( ( clnt = PtConnectionFindName( name, 0, 0 ) )
!= 0 ) {
struct MyReply reply;
int result = PtConnectionSend( clnt, msgtype,
&msg, &reply, sizeof(msg),
sizeof(reply) );
PtConnectionClientDestroy( clnt );
if ( result == 0 )
PtExit( reply.status );
}
}
if ( msgtype == MY_MSGTYPE_EXIT ) {
fputs( "Can’t tell it to exit; it’s not running\n",
stderr );
PtExit( EXIT_FAILURE );
}
if ( document )
OpenNewDocument( document );
return Pt_CONTINUE;
}
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/neutrino.h> /* Needed for MsgSend() */
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "globals.h"
#include "abimport.h"
#include "proto.h"
int
send_msg_to_b( PtWidget_t *widget,
ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
char *a_message;
return( Pt_CONTINUE );
You can create your own channel and call MsgReceive() on it, but
remember that your application and its interface will be blocked until
that process sends a message. It’s better to use an input handler as
described in this section.
data A pointer to any extra data you want to pass to the input
handler.
Pt_CONTINUE
The input handler doesn’t recognize the message. If
there are other input handlers attached to the same
process ID, they’re called. If there are no input handlers
attached specifically to this process ID, or if all input
handlers attached specifically to this process ID return
Pt_CONTINUE, the library looks for input handlers
attached to rcvid 0. If all the input handlers return
Pt_CONTINUE, the library replies to the message with
an ENOSYS.
Pt_END The message has been recognized and processed and the
input handler needs to be removed from the list. No
other input handlers are called for this message.
Pt_HALT The message has been recognized and processed but the
input handler needs to stay on the list. No other input
handlers are called for this message.
This buffer might not be large enough to hold the entire message. One
way of handling this is to have the first few bytes of the message
indicate the message type and from that determine how big the
message should be. Once you know the message size, you can:
• Copy the part you’ve already received into a new buffer. Get the
rest of the message by calling MsgReadv(). Add the rest of the
message to the first part.
Alternatively, you can set the event buffer to be the size of the largest
message your application will receive (if known). This can be done by
calling PtResizeEventMsg(). You’d typically call this before you
expect to receive any messages.
log_msglen = msg_offset+log->msg_len;
{
struct log_message *log_msg =
(struct log_message *)alloca(log_msglen);
if (log_msg == NULL ||
MsgRead( rcvid, log_msg, log_msglen, 0) == -1)
{
status = errno;
MsgError( rcvid, status);
return Pt_HALT; /* bail out */
}
log = log_msg;
}
add_msg(text, log);
status = 0;
MspReply( rcvid, 0, 0, 0);
}
return Pt_HALT;
}
Photon pulses
In addition to synchronous message-passing, Photon supports pulses.
A process that wants to notify another process but doesn’t want to
wait for a reply can use a Photon pulse. For example, a server can use
a pulse to contact a client process in a situation where sending a
message would leave both SEND-blocked (and hence deadlocked).
A Photon pulse is identified by a negative PID that can be used as the
pid argument to PtAppAddInput(). This PID is local to your
application. If you want another process to send pulses to you, you
must “arm” the pulse using PtPulseArm(). This creates a
PtPulseMsg_t object that can be sent to the other process in a
message. The other process will then be able to send pulses by calling
MsgDeliverEvent() function.
3 Send the pulse message to the process that will deliver it.
Before exiting, the recipient process should tell the delivering process
to stop sending pulses.
Creating a pulse
To create a Photon pulse, call PtAppCreatePulse():
priority The priority of the pulse. If this is -1, the priority of the
calling program is used.
Arming a pulse
Arming the pulse fills in the sigevent structure, which can be used
for most of the QNX Neutrino calls that take this type of argument.
There’s nothing wrong with having more than one process deliver the
same pulse, although the recipient won’t be able to tell which process
sent it.
Destroying a pulse
When your application no longer needs the pulse, it can be destroyed
by calling PtAppDeletePulse():
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "abimport.h"
#include "proto.h"
pid_t pulse;
if ( mqd >= 0 )
mq_close( mqd );
return( Pt_CONTINUE );
}
Processing signals
If your application needs to process signals, you’ll need to set up a
signal handler. The problem is that you can’t call Photon functions
from a signal handler because the widget library isn’t signal-safe or
reentrant.
To get around this problem, the Photon library includes a signal
handler. You register a signal-processing function, and Photon calls it
after
In this chapter. . .
Overview 491
Background processing 492
Work procedures 493
Threads 499
Overview
When you have to perform an operation that takes a long time to
execute, it’s not a good idea to implement it as a simple callback.
During the time the callback is executing, the widgets in your
application can’t repair damage and they won’t respond to user input
at all. You should develop a strategy for handling lengthy operations
within your application that involves returning from your callback as
quickly as possible.
Returning from your callback allows the widgets to continue to
update themselves visually. It also gives some visual feedback if the
user attempts to do anything. If you don’t want the user to be able to
perform any UI operations during this time, you should deactivate the
menu and command buttons. You can do this by setting the
Pt_BLOCKED flag in the application window widget’s
Pt_ARG_FLAGS resource.
You might consider one of several different mechanisms for dealing
with parallel operations:
• If you can’t break the operation into pieces, process Photon events
while the operation continues; see “Background processing,”
below.
• If you can break the operation into small chunks, you may wish to
have a function that keeps track of the current state and executes
one small chunk of the operation at a time. You can then set up a
timer widget and attach it to a callback that invokes the function
whenever the timer goes off. Or you may call the function from
within a work procedure. These methods are especially effective
for iterative operations where the function may be executed once
per iteration. See “Work procedures,” below.
• Spawn another process in the callback, and have the other process
return its results to the application by sending it messages. In this
Background processing
If a lengthy operation can’t be easily decomposed, and you don’t want
to use multiple threads, you should at least call
PtBkgdHandlerProcess() to process Photon events so that the GUI
doesn’t appear to be frozen.
If the operation is very lengthy, you can call PtBkgdHandlerProcess()
within a loop. How often you need to call PtBkgdHandlerProcess()
depends on what your application is doing. You should also find a
way to let the user know what progress the operation is making.
For example, if you’re reading a large directory, you could call the
background handler after reading a few files. If you’re opening and
processing every file in a directory, you could call
PtBkgdHandlerProcess() after each file.
• PtBkgdHandlerProcess()
• PtFileSelection()
• PtProcessEvent()
• PtSpawnWait()
Work procedures
A work procedure is run whenever there are no messages for your
application to respond to. In every iteration of the Photon
event-handling loop, this procedure is called if no messages have
arrived (rather than block on a MsgReceive() waiting for more
messages). This procedure will be run very frequently, so keep it as
short as possible.
There is one exception to this rule. If the work procedure that’s at the
top of the stack is running already, the next one is called. This is only
possible if the already running procedure allows the Photon library to
start another one, perhaps by calling a modal function like
PtModalBlock(), PtFileSelection() or PtAlert(), or calling PtLeave()
while you have other threads ready to process events.
void PtAppRemoveWorkProc(
PtAppContext_t app_context,
PtWorkProcId_t *WorkProc_id );
#include <Pt.h>
#include <stdlib.h>
PtWorkProcId_t *work_id;
} CountdownClosure_t;
if (dialog)
{
dialog->widget = window =
PtCreateWidget(PtWindow, parent, 0, NULL);
nargs = 0;
PtSetArg(&args[nargs], Pt_ARG_GROUP_ORIENTATION,
Pt_GROUP_VERTICAL, 0); nargs++;
PtSetArg(&args[nargs], Pt_ARG_GROUP_VERT_ALIGN,
Pt_GROUP_VERT_CENTER, 0); nargs++;
group = PtCreateWidget(PtGroup, window, nargs, args);
nargs = 0;
dim.w = 200;
dim.h = 100;
PtSetArg(&args[nargs], Pt_ARG_DIM, &dim, 0); nargs++;
PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING,
"Counter: ", 0); nargs++;
dialog->label = PtCreateWidget(PtLabel, group,
nargs, args);
nargs = 0;
PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING, "Stop", 0);
nargs++;
dialog->ok_button = PtCreateWidget(PtButton, group,
1, args);
}
return dialog;
}
call = call;
if (!closure->done) {
PtAppRemoveWorkProc(NULL, closure->work_id);
}
PtDestroyWidget(closure->dialog->widget);
free(closure->dialog);
free(closure);
return (Pt_CONTINUE);
}
int
count_cb(void *data)
{
CountdownClosure_t *closure =
(CountdownClosure_t *)data;
char buf[64];
int finished = 0;
if ( closure->value++ == 0 || closure->value %
1000 == 0 )
{
sprintf(buf, "Counter: %d", closure->value);
PtSetResource( closure->dialog->label,
Pt_ARG_TEXT_STRING, buf, 0);
}
if ( closure->value == closure->maxvalue )
{
closure->done = finished = 1;
PtSetResource( closure->dialog->ok_button,
Pt_ARG_TEXT_STRING, "Done", 0);
}
w = w; call = call;
dialog = create_working_dialog(parent);
if (dialog)
{
CountdownClosure_t *closure =
(CountdownClosure_t *)
malloc(sizeof(CountdownClosure_t));
if (closure)
{
PtWorkProcId_t *id;
closure->dialog = dialog;
closure->value = 0;
closure->maxvalue = 200000;
closure->done = 0;
closure->work_id = id =
PtAppAddWorkProc(NULL, count_cb, closure);
PtAddCallback(dialog->ok_button, Pt_CB_ACTIVATE,
done, closure);
PtRealizeWidget(dialog->widget);
}
}
return (Pt_CONTINUE);
}
if (PtInit(NULL) == -1)
exit(EXIT_FAILURE);
dim.w = 200;
dim.h = 100;
PtSetArg(&args[0], Pt_ARG_DIM, &dim, 0);
if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT,
1, args)) == NULL)
PtExit(EXIT_FAILURE);
callbacks[0].data = window;
n = 0;
PtSetArg(&args[n++], Pt_ARG_TEXT_STRING, "Count Down...", 0);
} else {
PtSetArg(&args[n++], Pt_ARG_TEXT_FONT, Helvetica14b, 0);
}
PtSetArg(&args[n++], Pt_CB_ACTIVATE, callbacks,
sizeof(callbacks)/sizeof(PtCallback_t));
PtCreateWidget(PtButton, window, n, args);
PtRealizeWidget(window);
PtMainLoop();
return (EXIT_SUCCESS);
}
Threads
Photon applications are event-driven and callback-based; whenever an
event arrives, the appropriate callback is invoked to handle it, and then
the control returns to the event loop to wait for the next event.
Because of this structure, most Photon applications are
single-threaded.
The Photon library lets you use threads, but in a way that minimizes
the overhead for single-threaded applications. The Photon library is
“thread-friendly,” rather than completely thread-safe the way printf()
and malloc() are thread-safe.
• Try to leave any non-Photon code that can take a while to complete
outside of the enter-leave section — otherwise it may
unnecessarily prevent other threads from doing their job.
Another choice is to have more than one Photon thread that processes
Photon events in your application. Here’s how:
• Call PtLeave() from the callback to give the other threads access to
the Photon library.
Unlocking the library lets other threads modify your widgets and
global variables while you’re not looking, so be careful.
If your callback allows other threads to process events while it’s doing
its lengthy operation, there’s a chance that the person holding the
mouse may press the same button again, invoking your callback
before its first invocation is complete.
You have to make sure that your application either handles this
situation properly, or prevents it from happening. Here are several
ways to do this:
• Block your button before the callback calls PtLeave(), and unblock
the button after calling PtEnter().
Or:
• Use a flag to tell the second invocation of the callback that it’s
already running.
Or:
• Use a counter if you want to count rather than just ignore any extra
button presses.
Or:
Realtime threads
Don’t make Photon calls from threads that must have deterministic
realtime behavior. It’s hard to predict how long PtEnter() will block
for; it can take a while for the thread that owns the lock to finish
processing the current event or call PtLeave(), especially if it involves
sending to other processes (like the window manager).
It’s better to have a “worker thread” that accepts requests from your
realtime threads and executes them in its own enter-leave section. A
condition variable — and possibly a queue of requests — is a good
way of sending these requests between threads.
If you’re using worker threads, and you need to use a condition
variable, call PtCondWait() instead of pthread_cond_wait() and a
separate mutex. PtCondWait() uses the Photon library lock as the
mutex and makes an implicit call to PtLeave() when you block, and to
PtEnter() when you unblock.
The threads block until:
Photon doesn’t start new threads for you if you run out of Photon
threads.
You can also turn a nonreader into a reader and back by passing a flag
to PtEnter() or PtLeave():
Pt_EVENT_PROCESS_ALLOW
Turn the calling thread into an event reader.
Pt_EVENT_PROCESS_PREVENT
Turn the calling thread into a nonreader.
If you don’t need to change the thread’s status (e.g. for a non-Photon
thread that never processes any events), don’t set either of these bits in
the flags.
If you’re calling PtLeave() in a callback because you’re about to do
something lengthy, pass Pt_EVENT_PROCESS_PREVENT to
PtLeave(). This tells the library that this thread isn’t going to process
events for a significant amount of time. Make sure to pass
Pt_EVENT_PROCESS_ALLOW to PtEnter() before returning from the
callback.
The reason is that certain Photon calls that normally are blocking
cause the calling thread to go to sleep (blocked indefinitely) if
PtExit() is pending (otherwise PtExit() would potentially block for
a long time). This also happens when a thread blocks before
another thread calls PtExit(); the blocked thread stays blocked
without returning from the blocking call. The sleeping threads
behave as if the scheduler didn’t give them any CPU cycles until
the entire process terminates. This allows the thread(s) that called
PtPreventExit() to finish their job as quickly as possible.
The list of Photon calls that make their calling threads sleep after
another thread has called PtExit() includes attempts to process
events, do anything modal, block on a condvar using PtCondWait()
or PtCondTimedWait(), calling PtEnter() or PtLeave(), and calling
PtExit().
• It may sometimes be difficult to make sure that your thread doesn’t
call any of those after calling PtPreventExit() — and if it does and
stays blocked without having a chance to call PtAllowExit(), your
process will lock up and you’ll have to kill it manually.
To prevent such situations, there’s a Pt_DELAY_EXIT flag that you
can pass to PtEnter() and PtLeave(). Doing it not only prevents
PtEnter() and PtLeave() from blocking indefinitely if another
thread has called PtExit(), but also implicitly calls PtPreventExit().
If your thread is put to sleep by a “sleep inducing” call, the library
knows to call PtAllowExit() for you. The only way to keep
Pt_DELAY_EXIT turned on is by making sure that you don’t call
any of the “sleep inducing” calls and pass Pt_DELAY_EXIT to
PtEnter() and PtLeave() each time you call them. The
Pt_DELAY_EXIT flag makes your “save file” callback as simple as
this:
my_callback( ... )
{
PtLeave( Pt_DELAY_EXIT );
save_file(); /* You’re safe here... */
PtEnter( 0 ); /* But this may never return
-- and that’s OK! */
}
You still have to make sure that save_file() doesn’t attempt any of
the “sleep inducing” calls. In particular, you can’t pop up a dialog
with an error message if something goes wrong. If you want to pop
up a dialog that will potentially sit on the screen for minutes or
hours, you have to do it before calling PtExit(), for example, by
using the Pt_ARG_WINDOW_MANAGED_FLAGS trick
mentioned above.
• If you attach a work procedure and you have more than one reader
thread, there’s a very narrow window where the work procedure
can be invoked right away instead of after you run out of events.
In this chapter. . .
PtRaw widget 511
Color 519
Drawing attributes 521
Arcs, ellipses, polygons, and rectangles 524
Lines, pixels, and pixel arrays 533
Text 535
Bitmaps 537
Images 539
Animation 546
Direct mode 552
Video memory offscreen 555
Alpha blending support 561
Chroma key support 562
Extended raster operations 563
Video modes 565
Gradients 567
Video overlay 568
Layers 572
PtRaw widget
The Pg routines in the Photon library are the lowest-level drawing
functions. They’re used by the widget library to draw the widgets.
You can use the Pg functions in a Photon application, but your
application has to:
You should use widgets whenever possible because they do all of the
above themselves.
If your application must do its own drawing, you should use the
PtRaw widget. It does the following:
Pt_ARG_RAW_INIT_F
An initialization function that’s called before the widget’s
extent is calculated.
Pt_ARG_RAW_EXTENT_F
If provided, calculates the widget’s extent when the widget is
moved or resized.
Pt_ARG_RAW_CALC_OPAQUE_F
Calculates the raw widget’s opacity tile list.
Pt_ARG_RAW_CONNECT_F
Called as the last stage in realizing the widget, just before any
required regions are created.
Pt_ARG_RAW_DRAW_F
Does the drawing.
Most of the time you’ll need to specify only the drawing function (see
below). You can use PhAB’s function editor (described in the Editing
Resources and Callbacks in PhAB chapter) to edit these resources —
but you must give the raw widget a unique instance name first. You
can also set these resources in your application’s code; for more
information, see “Function resources” in the Manipulating Resources
in Application Code chapter.
For information on PtRaw’s resources, see the Photon Widget
Reference.
• The origin for the drawing primitives is the top left corner of the
canvas of the raw widget’s parent, not the raw widget itself. You’ll
need to translate the coordinates.
• The raw widget can draw beyond its canvas, but it’s not a good
idea. You should set up clipping in the drawing function.
Translating coordinates
The origin for the drawing primitives is the upper left corner of the
raw widget’s parent’s canvas. You’ll probably find it easier to use the
upper left corner of the raw widget’s canvas as the origin.
Once you’ve determined the raw widget’s canvas, you can do one of
the following:
• Add the coordinates of the upper left corner of the raw widget’s
canvas to any coordinates passed to the drawing primitives. For
example, to draw an ellipse centered at (80, 60) relative to the raw
widget’s canvas:
PhPoint_t c1 = { 80, 60 };
PhPoint_t r = { 72, 52 };
c1.x += raw_canvas.ul.x;
c1.y += raw_canvas.ul.y;
PgSetFillColor(Pg_YELLOW);
PgDrawEllipse ( &c1, &r, Pg_DRAW_FILL );
PgSetFillColor(Pg_YELLOW);
PgDrawEllipse ( &c1, &r, Pg_DRAW_FILL );
Be sure to restore the old translation before leaving the raw widget’s
drawing function. Here’s one way to do it:
/* Restore the translation by subtracting the
coordinates of the raw widget’s canvas. */
raw_canvas.ul.x *= -1;
raw_canvas.ul.y *= -1;
PgSetTranslation (&raw_canvas.ul, Pg_RELATIVE);
Clipping
As mentioned above, it’s possible to draw beyond the raw widget’s
extent in its drawing function, but it’s not a good thing to do:
• If the raw drawing beyond the raw widget’s extent is damaged but
the raw widget itself isn’t, the raw widget’s drawing function isn’t
called, so the damage won’t be repaired.
The damaged areas are relative to the raw widget’s parent. At least
one of them intersects the raw widget, but some of them might not.
If there’s more than one tile in the linked list, the first one covers the
entire area covered by the rest. Either use the first tile and ignore the
rest, or ignore the first and use the rest:
void rawDrawFunction (PtWidget_t *widget,
PhTile_t *damage)
{
if (damage->next != NULL) {
damage = damage->next;
}
.
.
.
damage = damage->next; /* Go on to the next tile. */
}
}
PhAddMergeTiles()
Merge two list tiles, eliminating overlap
PhCoalesceTiles()
Combine a list of tiles
PhDeTranslateTiles()
Subtract x and y offsets from the vertices of a list
of tiles
PhIntersectTilings()
Determine the intersection of two lists of tiles
PhTranslateTiles()
Add x and y offsets to the vertices of a list of tiles
c1.x += raw_canvas.ul.x;
c1.y += raw_canvas.ul.y;
PgSetFillColor(Pg_YELLOW);
PgDrawEllipse ( &c1, &r, Pg_DRAW_FILL );
c2.x += raw_canvas.ul.x;
c2.y += raw_canvas.ul.y;
PgSetFillColor(Pg_RED);
PgDrawEllipse ( &c2, &r, Pg_DRAW_FILL );
PtClipRemove ();
}
PgSetFillColor(Pg_YELLOW);
PgDrawEllipse ( &c1, &r, Pg_DRAW_FILL );
PgSetFillColor(Pg_RED);
PgDrawEllipse ( &c2, &r, Pg_DRAW_FILL );
raw_canvas.ul.x *= -1;
raw_canvas.ul.y *= -1;
PgSetTranslation (&raw_canvas.ul, Pg_RELATIVE);
PtClipRemove ();
}
Color
Colors are specified in the Photon microGUI with the PgColor_t
type. The library and graphics drivers interpret this data type
according to the current color model (described in the documentation
for PgColor_t).
The default color model, Pg_CM_PRGB, uses a 32-bit
Red-Green-Blue (RGB) representation:
PgBackgroundShadings()
Calculate top and bottom shading colors
PgGetColorModel()
Get the current color model
PgSetColorModel()
Set the current color model
Drawing attributes
When doing raw drawing, you can set various attributes, including
fonts, palettes, fill colors, line colors and styles, and text colors. The
attributes that you set affect all raw drawing operations until you set
them again. For example, the line color affects all lines, pixels, and
bitmaps that you draw using the drawing primitives.
You don’t need to set these attributes if you’re using widgets; the
drawing attributes are set based on the widgets’ definitions and
resources.
However, in all other cases you should set these attributes before you
begin drawing. The defaults are undefined and drawing before setting
the relevant attributes may produce unexpected results.
General attributes
The functions that set general drawing attributes are:
PgDefaultMode()
Reset draw mode and plane mask to their default values
PgSetDrawMode()
Set draw mode
PgSetPlaneMask()
Protect video memory from being modified
Text attributes
The text attributes affect all the text that you draw by calling the
drawing primitives described in “Text,” below. The functions that set
text attributes are:
PgDefaultText()
Reset the text attribute to its system default
PgSetTextColor()
Set text color
PgSetTextDither()
Set text dither pattern
PgSetTextTransPat()
Set draw transparency
PgSetTextXORColor()
Set a color for XOR drawing
PgSetUnderline()
Set colors for underlining text
Fill attributes
The fill attributes affect all the drawing that you do by calling the
primitive functions described in
• Text
• Bitmaps
PgDefaultFill()
Reset the fill attribute to its default value
PgSetFillColor()
Set exact fill color
PgSetFillDither()
Set specific dither pattern and colors
PgSetFillTransPat()
Set draw transparency
PgSetFillXORColor()
Set a color for XOR drawing
• Text
• Bitmaps
PgDefaultStroke()
Reset the stroke attribute to its system default
PgSetStrokeCap()
Set what the ends of lines look like
PgSetStrokeColor()
Set the color of subsequent outlines
PgSetStrokeDither()
Apply a color pattern to outlines
PgSetStrokeTransPat()
Use a masking pattern to set draw transparency on outlines
PgSetStrokeXORColor()
Use the XOR of a color to draw outlines
PgSetStrokeDash()
Set dashed lines
PgSetStrokeJoin()
Set how lines are joined
PgSetStrokeWidth()
Set line thickness
PgSetStrokeFWidth()
Set line thickness
• rectangles
• rounded rectangles
• Create a PtRaw widget and call the primitives in its draw function.
See the section on the PtRaw widget earlier in this chapter.
Or:
Rectangles
You can draw rectangles, using the current drawing attributes, by
calling PgDrawIRect() or PgDrawRect().
PgDrawRect() uses a PhRect_t structure (see the Photon Library
Reference) for the rectangle coordinates, while PgDrawIRect() lets
you specify the coordinates individually. Use whichever method you
want.
The following example draws a rectangle that’s filled, but not stroked
(i.e. it has no border):
void DrawFillRect( void )
{
PgSetFillColor( Pg_CYAN );
PgDrawIRect( 8, 8, 152, 112, Pg_DRAW_FILL );
}
PgSetFillColor( Pg_CYAN );
PgDrawRect( &rect, Pg_DRAW_FILL );
}
Rounded rectangles
Rounded rectangles are programmed almost the same way as
rectangles — just call PgDrawRoundRect() with a PhPoint_t
parameter to indicate, in pixels, the roundness of the rectangle
corners. The radii are truncated to the rectangle’s sides.
The following example draws a black rounded rectangle with five
pixels worth of rounding at the corners:
void DrawStrokeRoundRect( void )
{
PhRect_t rect = { {20, 20}, {100, 100} };
PhPoint_t radii = { 5, 5 };
PgSetStrokeColor( Pg_BLACK );
PgDrawRoundRect( &rect, &radii, Pg_DRAW_STROKE );
}
This code draws a dark grey beveled box with a green and red bevel
that’s four pixels wide:
A beveled box.
void DrawBeveled() {
PgSetFillColor( Pg_GREEN );
PgSetStrokeColor( Pg_GREY );
PgDrawBeveled( &clipped_rect, &clipping, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_CLIP );
PgDrawBeveled( &rounded_rect, &rounding, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_ROUND );
PgSetFillColor( Pg_CYAN );
PgSetStrokeColor( Pg_GREY );
PgDrawBeveled( &rup, NULL, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_AUP );
PgDrawBeveled( &rdown, NULL, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_ADOWN );
PgDrawBeveled( &rleft, NULL, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_ALEFT );
PgDrawBeveled( &rright, NULL, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_ARIGHT );
}
If you want to draw an arrow that fits inside a given rectangle (for
example, the arrow for a scrollbar), call PgDrawArrow().
Polygons
You can create polygons by specifying an array of PhPoint_t points.
If you use Pg_CLOSED as part of the flags, the last point is
automatically connected to the first point, closing the polygon. You
can also specify points relative to the first point (using
Pg_POLY_RELATIVE).
The following example draws a blue-filled hexagon with a white
outline:
void DrawFillStrokePoly( void )
{
PhPoint_t start_point = { 0, 0 };
int num_points = 6;
PhPoint_t points[6] = {
{ 32,21 }, { 50,30 }, { 50,50 },
{ 32,59 }, { 15,50 }, { 15,30 }
};
PgSetFillColor( Pg_BLUE );
PgSetStrokeColor( Pg_WHITE );
PgDrawPolygon( points, num_points, start_point,
Pg_DRAW_FILL_STROKE | Pg_CLOSED );
}
Overlapping polygons
Polygons that overlap themselves are filled using the so-called
even-odd rule: if an area overlaps an odd number of times, it isn’t
filled. Another way of looking at this is to draw a horizontal line
across the polygon. As you travel along this line and cross the first
line, you’re inside the polygon; as you cross the second line, you’re
outside. As an example, consider a simple polygon:
• When you cross an odd number of lines, you’re inside the polygon,
so the area is filled.
• arcs
• circles
• ellipses
• elliptical arcs
• chords
• pie slices
To draw a full circle or ellipse, specify the same value in bi-grads for
the start and end angles. For example:
void DrawFullCurves( void )
{
PhPoint_t circle_center = { 150, 150 },
ellipse_center = { 150, 300 };
PhPoint_t circle_radii = { 100, 100 },
ellipse_radii = { 100, 50 };
PgSetStrokeColor( Pg_WHITE );
PgSetFillColor( Pg_BLACK );
PgDrawArc( ¢er, &radii, 0, 0x2000,
Pg_DRAW_FILL_STROKE | Pg_ARC_CHORD );
}
PgSetStrokeColor( Pg_WHITE );
PgSetFillColor( Pg_BLACK );
PgDrawArc( &pie_center, &pie_radii, 0, 0x2000,
Pg_DRAW_FILL_STROKE | Pg_ARC_PIE );
short y Y position.
PgDrawBezier(), PgDrawBeziermx()
Draw a stroked and/or filled Bézier curve
PgDrawGrid()
Draw a grid
PgDrawLine(), PgDrawILine()
Draw a single line
PgDrawPixel(), PgDrawIPixel()
Draw a single point
PgDrawPixelArray(), PgDrawPixelArraymx()
Draw multiple points
PgDrawTrend(), PgDrawTrendmx()
Draw a trend graph
Text
There are various routines that draw text, depending on your
requirements:
PgDrawMultiTextArea()
Draw multiline text in an area
PgDrawString(), PgDrawStringmx()
Draw a string of characters
PgDrawText(), PgDrawTextmx()
Draw text
PgDrawTextArea()
Draw text within an area
PgDrawTextChars()
Draw the specified number of text characters
PgExtentMultiText()
Calculate the extent of a multiline text string
PgExtentText()
Calculate the extent of a string of text
Text is drawn using the current text attributes; for more information,
see “Text attributes,” above. If you set flags to Pg_BACK_FILL, the
text’s extent is filled according to the current fill attributes (see “Fill
attributes”). If you define an underline with PgSetUnderline(), the
underline is drawn under the text and on top of the backfill.
For example, to print black text in 18-point Helvetica:
if(PfGenerateFontName("Helvetica", 0, 18,
Helvetica18) == NULL) {
perror("Unable to generate font name");
} else {
PgSetFont( Helvetica18 );
}
PgSetTextColor( Pg_BLACK );
PgDrawText( s, strlen( s ), &p, 0 );
}
if(PfGenerateFontName("Helvetica", 0, 18,
Helvetica18) == NULL) {
perror("Unable to generate font name");
} else {
PgSetFont( Helvetica18 );
}
PgSetTextColor( Pg_BLACK );
PgSetFillColor( Pg_CYAN );
PgDrawText( s, strlen( s ), &p, Pg_BACK_FILL );
}
if(PfGenerateFontName("Helvetica", 0, 18,
Helvetica18) == NULL) {
perror("Unable to generate font name");
} else {
PgSetFont( Helvetica18 );
}
PgSetTextColor( Pg_BLACK );
PgSetUnderline( Pg_RED, Pg_TRANSPARENT, 0 );
PgDrawText( s, strlen( s ), &p, 0 );
PgSetUnderline( Pg_TRANSPARENT, Pg_TRANSPARENT, 0 );
}
if(PfGenerateFontName("Helvetica", 0, 18,
Helvetica18) == NULL) {
perror("Unable to generate font name");
} else {
PgSetFont( Helvetica18 );
}
PgSetTextColor( Pg_BLACK );
PgSetFillColor( Pg_CYAN );
PgSetUnderline( Pg_RED, Pg_TRANSPARENT, 0 );
PgDrawText( s, strlen( s ), &p, Pg_BACK_FILL );
PgSetUnderline( Pg_TRANSPARENT, Pg_TRANSPARENT, 0 );
}
Bitmaps
Bitmaps are drawn using the current text state. If you set flags to
Pg_BACK_FILL, the blank pixels in the image are drawn using the
current fill state. The drawing primitives for bitmaps are:
PgDrawBitmap(), PgDrawBitmapmx()
Draw a bitmap
PgDrawRepBitmap(), PgDrawRepBitmapmx()
Draw a bitmap several times
PgSetTextColor( Pg_CELIDON );
PgDrawBitmap( TestBitmap, 0, &p, &TestBitmapSize,
TestBitmapBPL, 0 );
}
PgSetTextColor( Pg_CELIDON );
PgSetFillColor( Pg_YELLOW );
PgDrawBitmap( TestBitmap, Pg_BACK_FILL, &p,
&TestBitmapSize, TestBitmapBPL, 0 );
}
A backfilled bitmap.
Images
The Photon microGUI supports these main types of images:
You can define any image by its pixel size, bytes per line, image data,
and format. Images can be stored in structures of type PhImage_t
(described in the Photon Library Reference). The type field of this
data structure identifies the type of image.
Palette-based images
Palette-based images provide a fast, compact method for drawing
images. Before drawing a palette-based image, you must set either a
hard palette or soft palette to define the colors for the image.
Setting a hard palette changes the physical palette. All colors set with
a PgSetFillColor() function are chosen from this palette. Other
processes continue to choose colors from the Photon microGUI’s
global palette and may appear incorrect. When you release the hard
If your physical palette uses more colors than your graphics card
supports, some colors are dropped, and the image won’t look as nice.
The image data (either bytes or nibbles) is an index into the current
palette. For example:
PgColor_t ImagePalette[256];
char *ImageData;
PhPoint_t ImageSize;
int ImageBPL;
Direct-color images
With direct-color images, every pixel can be any color. But compared
to palette-based images, the image data is larger and the image may
take longer to draw. You can choose from several types of direct-color
images, listed in the description of PhImage_t in the Photon Library
Reference; each has a different image-pixel size and color accuracy.
Gradient-color images
With gradient-color images, colors are algorithmically generated as a
gradient between two given colors.
Creating images
To create a PhImage_t structure:
• Call PhCreateImage()
Or:
Caching images
The image_tag and palette_tag members of the PhImage_t structure
are used for caching images when dealing with remote processes via
phrelay (see the QNX Neutrino Utilities Reference) for example,
when using phindows.
These tags are cyclic-redundancy checks (CRCs) for the image data
and the palette, and can be computed by PtCRC() or PtCRCValue() If
these tags are nonzero, phindows and phditto cache the images.
Before sending an image, phrelay sends its tag. If phindows finds
the same tag in its cache, it uses the image in the cache. This scheme
reduces the amount of data transmitted.
You don’t need to fill in the tags if the images don’t need to be saved
in the cache. For example, set the tags to 0 if you’re displaying
animation by displaying images, and the images never repeat.
Transparency in images
If you want parts of an image to be transparent, you can:
Using chroma
To make a given color transparent in an image, using chroma if
possible, call PhMakeTransparent(), passing it the image and the
RGB color that you want to be made transparent.
Displaying images
There are various ways to display an image:
The mx versions of these functions place the address of the image into
the draw buffer in your application’s data space. When the draw
buffer is flushed, the entire image is copied to the graphics driver. The
non-mx versions copy the image itself into the draw buffer.
You can speed up the drawing by using shared memory. Call
PgShmemCreate() to allocate the image data buffer:
my_image->image = PgShmemCreate( size, NULL );
If you do this, the image data isn’t copied to the graphics driver.
Manipulating images
The following functions let you manipulate images:
PiDuplicateImage()
Duplicate an image
PiFlipImage() Flip all or part of an image
PiGetPixel() Retrieve the value of a pixel within an image
PiGetPixelFromData()
Retrieve a value from a run of pixels
PiGetPixelRGB()
Retrieve the RGB value of a pixel within an image
PiSetPixel() Alter the value of a pixel within an image
PiSetPixelInData()
Set the value of a pixel in a run of pixels
Releasing images
The PhImage_t structure includes a flags member that can make it
easier to release the memory used by an image. These flags indicate
which members you would like to release:
• Ph_RELEASE_IMAGE
• Ph_RELEASE_PALETTE
• Ph_RELEASE_TRANSPARENCY_MASK
• Ph_RELEASE_GHOST_BITMAP
my_image->flags = Ph_RELEASE_IMAGE |
Ph_RELEASE_PALETTE |
Ph_RELEASE_TRANSPARENCY_MASK |
Ph_RELEASE_GHOST_BITMAP;
When should you set the release flags? When you know that the
image is referred to only by one entity. For example, if one widget
will be using an image, then it should free the image once it’s done
with it. If you set the release flags appropriately, prior to setting the
image resource, then this will happen automatically — that is, the
widget will free the image and data when it’s destroyed, or you apply
a new setting for the resource.
If multiple widgets use the same image (they have their own copies of
the image structure but share the data to conserve memory), then you
need to be a little more clever and make sure the image is freed only
when all the widgets are done with it, and never before. There are a
number of ways to accomplish this. For example, you could:
• Alternatively, if you know one widget will outlast all the others
using the image, then set the release flags in the structure prior to
setting the image resource of that widget. All the rest should have
the flags clear. Note that if you change the image resource on that
widget, however, the image will be freed, thus invalidating all the
other widgets’ references to it!
Animation
This section describes how you can create simple animation. There
are two basic steps:
It’s better to use images for animation than bitmaps, as images aren’t
transparent (provided you haven’t created a transparency mask). This
means that the background doesn’t need to be redrawn when
replacing one image with another. As a result, there’s no flicker when
you use images. For other methods of eliminating flicker, see
“Flickerless animation”, below.
It’s also possible to create animation by using a PtRaw widget and the
Photon drawing primitives. See “PtRaw widget”, earlier in this
chapter.
/* global data */
PhImage_t *images[4];
ApDBase_t *database;
int cur_image = -1,
num_images = 4;
int
app_init( int argc, char *argv[])
{
int i;
char image_name[15];
return (PT_CONTINUE);
}
Using a file
You can also load the snapshots from a file into a PhImage_t
structure, by using the PxLoadImage() function. This function
supports a number of formats, including GIF, PCX, JPG, BMP, and
PNG. For a complete list, see /usr/photon/dll/pi_io_*.
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "globals.h"
#include "abimport.h"
#include "proto.h"
int
display_image( PtWidget_t *widget,
ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
cur_image++;
if (cur_image >= num_images)
{
cur_image=0;
}
return( Pt_CONTINUE );
}
ABW_base_image is the widget name of the PtLabel widget in
which the animation appears.
Flickerless animation
There are two ways to eliminate flicker in animation:
PtOSContainer
When you do animation in a child of an offscreen-context container,
the PtOSContainer renders the draw stream into offscreen video
memory, taking advantage of any hardware-acceleration features that
the graphics driver supports. The graphics hardware can then blit the
image directly onto the screen, resulting in flicker-free widgets and/or
animation.
Memory-context functions
You can call these functions to use a memory context to reduce
flickering:
PmMemCreateMC()
Create a memory context
PmMemFlush()
Flush a memory context to its bitmap
PmMemReleaseMC()
Release a memory context
PmMemSetChunkSize()
Set the increment for growing a memory context’s draw buffer
PmMemSetMaxBufSize()
Set the maximum size of a memory context’s draw buffer
PmMemSetType()
Set the type of a memory context
PmMemStart()
Make a memory context active
PmMemStop()
Deactivate a memory context
PmMemoryContext_t * PmMemCreateMC(
PhImage_t *image,
PhDim_t *dim,
PhPoint_t *translation );
The image structure must at least specify the type and size members.
The image data buffer is optional, but if you want it in shared
memory, you must provide it. The image type must be either
Pg_IMAGE_DIRECT_888 or Pg_IMAGE_PALETTE_BYTE.
Once you’ve created the memory context:
Direct mode
In normal (nondirect) mode, an application sends drawing requests to
the Photon manager. The graphics driver blocks on the Photon
manager.
While in direct mode, the application has complete control over the
display, since no other applications are able to be serviced by the
graphics driver. The graphics driver’s region is also no longer
sensitive to draw events (this way the Photon manager discards all
other applications’ requests for rendering services to this driver). The
other benefit with this mode is that graphical services are no longer
sent through the Photon event space, so performance is improved.
The drawback for this mode is that applications that expect to capture
draw events can’t record the application’s view.
For convenience, a new context type, called a PdDirectContext_t,
has been created. This context, when activated, becomes the default
context for the application, so all other Photon Pg* calls work
correctly while in this mode.
While in this mode, the origin of all drawing operations is the upper
left corner of the display, since the requests are no longer clipped or
translated by the Photon event space. Your application can still
translate and clip the events by calling PgSetTranslation() and
PgSetClipping() if necessary.
PdCreateDirectContext()
Create a direct-mode context
PdDirectStart()
Enter direct mode
PdDirectStop()
Leave direct mode
PdGetDevices()
Get region IDs for the currently available draw devices
PdReleaseDirectContext()
Leave direct mode and release the direct-mode context
PdSetTargetDevice()
Set the target device
PgWaitVSync()
Wait for vertical synchronization
• When you enter or leave direct mode, all video RAM contexts
(except the display), are destroyed on the driver side (an OSINFO
event is shot out by the driver so applications are notified and can
reinitialize any video memory contexts). This includes video RAM
• When you leave direct mode, an expose event is also sent out by
the driver, so all other applications redraw themselves.
Example
Here’s how to get the address of any video memory context (including
the display, which is considered to be one).
If you create a direct context by calling PdCreateDirectContext(), and
then enter direct mode by calling PdDirectStart(), your application
“owns” the graphics driver (PgFlush() goes to the video driver
directly, instead of to the Photon server). You don’t need to be in
direct mode to get a pointer to offscreen RAM, but you do need to be
to get a pointer to the primary display.
Here’s some pseudo-code:
PdCreateOffscreenContext()
Create an offscreen context in video RAM
PdDupOffscreenContext()
Duplicate an offscreen context
PdGetOffscreenContextPtr()
Create a shared memory object reference to an offscreen
context
PdOffscreenContext_t
Data structure that describes an offscreen context
PdSetOffscreenTranslation()
Set the translation for an offscreen context
PdSetTargetDevice()
Set the target device
PgContextBlit()
Copy data from a rectangle in one context to another context
PgContextBlitArea()
Copy data from an area in one context to another context
PgSwapDisplay()
Point the CRT of the video display at a given context
PgWaitHWIdle()
Wait until the video driver is idle
PhDCRelease()
Release a draw context
#include <Pt.h>
#include <photon/PxImage.h>
if(PtInit(NULL))
return(-1);
if(argv[optind])
{
PxMethods_t methods;
PhImage_t *image;
memset(&methods,0,sizeof(methods));
methods.px_alloc = my_alloc;
methods.flags = PX_DIRECT_COLOR;
if((context = PdCreateOffscreenContext(image->type,
image->size.w,image->s
{
PtArg_t args[4];
PtWidget_t *window;
PhDrawContext_t *dc = PhDCSetCurrent(context);
if(!dim.w || !dim.h)
dim = image->size;
PgSetFillColor(Pg_WHITE);
PgDrawIRect(0,0,image->size.w - 1,image->size.h - 1,Pg_DR
PgDrawPhImagemx(NULL,image,0);
PgFlush();
PgWaitHWIdle();
PhDCSetCurrent(dc);
image->flags |= Ph_RELEASE_IMAGE_ALL;
PhReleaseImage(image);
free(image);
PtSetArg(&args[0],Pt_ARG_DIM,&dim,0);
PtSetArg(&args[1],Pt_ARG_WINDOW_TITLE,argv[optind],0);
if((window = PtCreateWidget(PtWindow,Pt_NO_PARENT,2,args
{
PhRect_t arect = { { 0,0 },{ 0,0 } };
PtSetArg(&args[1],Pt_ARG_RAW_DRAW_F,raw_draw,0);
PtSetArg(&args[2],Pt_ARG_ANCHOR_FLAGS,
Pt_LEFT_ANCHORED_LEFT | Pt_RIGHT_ANCHORED_RIGHT
Pt_TOP_ANCHORED_TOP | Pt_BOTTOM_ANCHORED_BOTTOM,
Pt_LEFT_ANCHORED_LEFT | Pt_RIGHT_ANCHORED_RIGHT
Pt_TOP_ANCHORED_TOP | Pt_BOTTOM_ANCHORED_BOTTOM)
PtSetArg(&args[3],Pt_ARG_ANCHOR_OFFSETS,&arect,0);
if(PtCreateWidget(PtRaw,Pt_DFLT_PARENT,4,args) != NUL
{
PtRealizeWidget(window);
PtMainLoop();
return(0);
}
}
}
}
}
return(-1);
}
PhRect_t src;
src.ul.x = src.ul.y = 0;
src.lr.x = context->dim.w - 1;
src.lr.y = context->dim.h - 1;
PgContextBlit(context,&src,PhDCGetCurrent(),PtCalcCanvas(widget,NU
}
Pg_VIDEO_MODE_SWITCHED
The graphics driver has changed video modes.
Pg_ENTERED_DIRECT
An application has entered direct mode.
Pg_EXITED_DIRECT
An application has left direct mode.
Pg_DRIVER_STARTED
The video driver has just started execution.
Offscreen locks
You generally use offscreen locks with pointers that you gained via
PdGetOffscreenContextPtr(). The locks ensure that:
• On a per-pixel basis
PgSetAlphaBlend()
Set the parameters for alpha blending simply
PgChromaOff()
Turn chroma key operations off
PgChromaOn()
Turn chroma operations on
PgSetChroma()
Set the chroma color and operation
Character Meaning
P Pattern
continued. . .
Character Meaning
S Source
D Destination
o OR
a AND
n NOT
x XOR
For example:
Pg_DrawModeS
Copy all source data.
Pg_DrawModePSo
Logically OR the source data with the pattern data.
PgSetDrawMode(Pg_DrawModePSo);
PgSetFillDither(Pg_BLUE,Pg_BLACK,Pg_PAT_CHECKB8);
PgContextBlit(context1, &rsrc, NULL, &rdst);
Video modes
A video mode describes what the display (what you see on your
monitor) looks like. The description includes:
Refresh rate How many times per second the phosphor on the
CRT of your monitor is updated (represented in Hz).
PdSetTargetDevice()
Set the target device
PgGetGraphicsHWCaps()
Determine the hardware capabilities
PgGetVideoMode()
Get the current video mode
PgGetVideoModeInfo()
Get information about a video mode
PgGetVideoModeList()
Query a graphics driver for a list of its supported video modes
PgSetVideoMode()
Set the current video mode
PgVideoModes_t ModeList;
PgVideoModeInfo_t ModeInfo;
PgDisplaySettings_t ModeSetting;
int i=0, done=0;
if (PgGetVideoModeList(&ModeList))
{
/* Error -- driver doesn’t support this. */
}
while (!done)
{
if (PgGetVideoModeInfo(ModeList.modes[i], &ModeInfo))
{
/* Error code */
}
i++;
if (i >= ModeList.num_modes)
{
/* Error -- Mode wasn’t found. */
done=1;
}
}
PgSetVideoMode (&ModeSetting);
Gradients
A gradient is a gradual blend of two colors. The Photon library
supports:
Driver-level gradients
Although the Photon library supports a large variety of gradients (see
PhImage_t), there are times when you would just want a simple
gradient to be rendered without having to store it in a PhImage_t. As
a result, some basic gradient rendering operations have been added to
the graphics driver:
PgDrawGradient()
Ask the graphics driver to render a gradient
Application-level gradients
These functions let you create your own gradients:
PgCalcColorContrast()
Compute light and dark colors to use for a gradient
PgContrastBevelBox()
Draw a beveled box with gradients and a given
level of contrast
PgDrawGradientBevelBox()
Draw a beveled box with gradients and two flat
colors
Video overlay
A video overlay scaler is a hardware feature that allows a rectangular
area of the visible screen to be replaced by a scaled version of a
different image. The prescaled video frames are typically stored in
offscreen memory, and are fetched from memory and overlaid on top
of the desktop display image in real time, by the overlay scaler.
Chroma keying is used to control what parts of the video frame are
visible. Typically, the application picks a color to be the chroma-key
color and draws a rectangle of this color where video content is to
appear. When another application’s window is placed on top of the
video playback application, the chroma-colored rectangle is obscured.
Since the video hardware is programmed to display video content
only where the chroma-key color is drawn, video doesn’t show
through where the chroma-colored rectangle is obscured.
The following functions and data types deal with video overlay:
PgConfigScalerChannel()
Configure a video overlay scaler channel
PgCreateVideoChannel()
Create a channel for video streaming
PgDestroyVideoChannel()
Destroy resources associated with a video channel
PgGetOverlayChromaColor()
Return the color used for video overlay chroma-key operations
PgGetScalerCapabilities()
Get the capabilities of a video overlay scaler
PgNextVideoFrame()
Get the index of the next video buffer to fill
PgScalerCaps_t
Data structure that describes video overlay scaler capabilities
PgScalerProps_t
Data structure that describes video overlay scaler properties
PgVideoChannel_t
Data structure that describes a video overlay channel
Example
#include <stdio.h>
#include <Ph.h>
void
grab_ptrs(PgVideoChannel_t *channel)
{
/* Buffers have moved; get the pointers again. */
ybuf0 = PdGetOffscreenContextPtr(channel->yplane1);
ybuf1 = PdGetOffscreenContextPtr(channel->yplane2);
ubuf0 = PdGetOffscreenContextPtr(channel->uplane1);
ubuf1 = PdGetOffscreenContextPtr(channel->uplane2);
vbuf0 = PdGetOffscreenContextPtr(channel->vplane1);
vbuf1 = PdGetOffscreenContextPtr(channel->vplane2);
if (channel->yplane1)
fprintf(stderr, "ybuf0: %x, stride %d\n", ybuf0,
channel->yplane1->pitch);
if (channel->uplane1)
fprintf(stderr, "ubuf0: %x, stride %d\n", ubuf0,
channel->uplane1->pitch);
if (channel->vplane1)
fprintf(stderr, "vbuf0: %x, stride %d\n", vbuf0,
channel->vplane1->pitch);
if (channel->yplane2)
fprintf(stderr, "ybuf1: %x, stride %d\n", ybuf1,
channel->yplane2->pitch);
if (channel->uplane2)
fprintf(stderr, "ubuf1: %x, stride %d\n", ubuf1,
channel->uplane2->pitch);
if (channel->vplane2)
fprintf(stderr, "vbuf1: %x, stride %d\n", vbuf1,
channel->vplane2->pitch);
}
void
overlay_example()
{
PgVideoChannel_t *channel;
PgScalerCaps_t vcaps;
PgScalerProps_t props;
unsigned char *ptr;
unsigned short *ptr16;
int i = 0, j, k, index;
int color;
PhDrawContext_t *old;
int rc;
if ((channel = PgCreateVideoChannel(
Pg_VIDEO_CHANNEL_SCALER, 0)) == NULL) {
perror("PgCreateVideoChannel");
exit(1);
}
/*
* Cycle through the available formats looking for the one
* we’re interested in.
*/
vcaps.size = sizeof (vcaps);
while (PgGetScalerCapabilities(channel, i++, &vcaps) == 0) {
if (vcaps.format == DATA_FORMAT)
break;
vcaps.size = sizeof (vcaps);
}
if (vcaps.format != DATA_FORMAT) {
fprintf(stderr, "Format not supported?\n");
exit(1);
}
props.viewport.lr.x = 600;
props.viewport.lr.y = 440;
props.src_dim.w = SRC_WIDTH;
props.src_dim.h = SRC_HEIGHT;
props.flags =
Pg_SCALER_PROP_SCALER_ENABLE |
Pg_SCALER_PROP_DOUBLE_BUFFER |
Pg_SCALER_PROP_DISABLE_FILTERING;
if (PgConfigScalerChannel(channel, &props) == -1) {
fprintf(stderr, "Configure channel failed\n");
exit(1);
}
grab_ptrs(channel);
/*
* This isn’t really necessary, since the video resources
* should automatically be released when the app exits
*/
PgDestroyVideoChannel(channel);
}
int
main(int argc, char *argv[])
{
PhAttach(NULL, NULL);
overlay_example();
Layers
Some display controllers allow you to transparently overlay multiple
"screens" on a single display. Each overlay is called a layer.
Layers can be used to combine independent display elements.
Because overlaying is performed by the graphics hardware, it can be
more efficient than rendering all of the display elements onto a single
layer. For example, a fast navigational display can be implemented
with a scrolling navigational map on a background layer, and pop-up
GUI elements, such as menus or a web browser, on a foreground layer.
Layer capabilities vary depending on the display controller and the
driver. Some display controllers don’t support layers. Different layers
on the same display may have different capabilities. You should use
PgGetLayerCaps() to determine whether a layer exists and which
features are supported by the layer.
Layers are indexed per-display, starting from 0, from back to front in
the default overlay order.
A layer is either active (shown) or inactive (hidden). It may not be
possible to activate a layer if its configuration is incomplete (if, for
example, the layer format is unspecified, or there aren’t enough
surfaces assigned to it). A layer’s configuration persists when it’s
inactive. After a video mode switch, all layers revert to their default
configuration.
The images on all the active layers of a display are combined, using
alpha blending, chroma keying, or both, to produce the final image on
the display.
Surfaces
The image on a layer is fetched from one or more offscreen contexts,
also called surfaces. The number of surfaces needed by a layer is
determined by the layer format. For example, a layer whose format is
Pg_LAYER_FORMAT_ARGB888 requires one surface, while a layer
whose format is Pg_LAYER_FORMAT_YUV420 requires three
surfaces for a complete image. The format of a layer is set using
PgSetLayerArg().
Viewports
Display
Destination viewport
Surface data
Source viewport
Layer API
The layer API includes:
PgGetLayerCaps()
Query the capabilities of a layer
PgCreateLayerSurface()
Create an offscreen context displayable by a layer
PgSetLayerSurface()
Display an offscreen context on a layer
PgSetLayerArg()
Configure a layer parameter
PgLockLayer()
Lock a layer for exclusive use by an application
PgUnlockLayer()
Release a locked layer
PgLayerCaps_t
Data structure that describes the capabilities for a layer
Using layers
To use layers, you typically do the following:
3 Allocate surfaces for the layer, and offscreen contexts for the
surfaces, by calling PgCreateLayerSurface().
9 If the layer format is one of the RGB or PAL8 formats, set the
current draw context to render into a surface’s associated draw
context(s), and then use the Pg* functions to draw into the
offscreen context.
10 If the layer format is YUV, and so on, you typically dump data
directly to the buffer (like the video channel buffers).
See the code below for an example of using the layers API.
Example
#include <errno.h>
#include <stdio.h>
#include <Ph.h>
int
FindFormatIndex(int layer, unsigned int format)
{
PgLayerCaps_t caps;
int format_idx = 0;
format_idx++;
}
return -1;
}
int
main(int argc, char **argv)
{
/*
* For best results, these values should match your video mode.
*/
#define LAYER_FORMAT Pg_LAYER_FORMAT_ARGB8888
#define SURFACE_WIDTH 1024
#define SURFACE_HEIGHT 768
PhDrawContext_t *olddc;
PhRid_t driver_rid = -1;
int layer_idx = -1;
int format_idx = -1;
int active = 1;
int i;
/*
* Arguments:
* -d <driver region>
* -l <layer index>
*/
if (layer_idx == -1) {
printf("Specify layer index.\n");
exit(-1);
}
if (driver_rid == -1) {
printf("Specify graphics driver region.\n");
exit(-1);
}
ph = PhAttach(NULL, NULL);
if (ph == NULL) {
perror("PhAttach");
exit(-1);
}
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_ACTIVE,
&active, sizeof(int));
/* End configuration */
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_END, 0, 0);
active = 0;
PgUnlockLayer(layer_idx);
if (scr) PhDCRelease(scr);
PhDCRelease(surf);
PhDetach(ph);
exit(0);
}
In this chapter. . .
Symbol metrics 583
Font function libraries 584
Font names 588
Writing text in a rectangular area 595
Repairing damage to proportional text 599
Although the Photon and font libraries provide many functions that
deal with fonts (see the Pf—Font Server chapter of the Photon Library
Reference), most of them are low-level routines that you probably
don’t need to use. This chapter describes the basics of using fonts.
Symbol metrics
Let’s start with some definitions:
Extent
X Max
Ascender
Baseline
Descender
Origin
Advance
Bearing x
(left bearing)
Symbol metrics.
phfont.so
io-graphics
These methods are made possible through the font server plugin,
phfont.so, which contains all common font server code. This
allows the memory footprint of the font server to be potentially much
smaller than it was before. The font library also allows you to
Now say you had only minimal resources. Your applications would
use the external font server phfont, with each application performing
a message pass to process fonts, which would require minimal
memory but maximum CPU usage.
phfont.so
phfont
Finally, you might determine that you need some applications to run
private font server instances to improve font rendering speed, while
others use the external font server, phfont.
phfont.so
io-graphics
phfont.so
phfont
Font names
A font is identified by its name, which can be in one of these forms:
To specify a font in the Photon API, you always use a stem name. You
should consider stem names to be constant identifiers, not modifiable
strings.
You can hard-code all references to fonts in a Photon application. But
your application can be more flexible if it uses the foundry name to
choose the best match from whatever fonts are available. That way,
there isn’t a problem if a particular font is eventually renamed,
removed, or replaced.
For example, the following call to PtAlert() uses the hard-coded stem
name helv14 to specify 14-point Helvetica:
answer = PtAlert(
base_wgt, NULL, "File Not Saved", NULL,
"File has not been saved.\nSave it?",
"helv14", 3, btns, NULL, 1, 3, Pt_MODAL );
You can get the available stem names from the names of the files in
${PHOTON_PATH}/font_repository — just remove any file
extension (e.g. .phf).
Alternately, if you have a $HOME/.ph directory, check in
$HOME/.ph/font/. The Photon microGUI creates this local file
only when needed, such as when you run the fontadmin utility (see
the QNX Neutrino Utilities Reference) to create your own personal
font configuration. Until the local file is created, the microGUI uses
the global file.
We’ve defined the FontName data type for you to use for the buffer
you pass to PfGenerateFontName(). It’s an array of size
MAX_FONT_TAG. For successful font programming, don’t use a font
identifier storage buffer that’s smaller than FontName.
FontDetails structure
Once you’ve got the list of fonts, you need to examine each
FontDetails structure in it to find the font you need and determine
the string to use as the stem name.
The FontDetails structure is defined in <photon/Pf.h>, and
contains at least these elements:
FontDescription desc
The foundry name or full descriptive name of the font, such as
Helvetica or Charter.
FontName stem
The short form. This provides a part of the stem name used by
the Photon API calls. For example, helv and char correspond
to Helvetica and Charter.
(You can use this approach even if you don’t use PfQueryFonts() to
find all the available fonts.)
Here’s the same call to PtAlert() as shown earlier, but this time it calls
PfGenerateFontName():
char Helvetica14[MAX_FONT_TAG];
if ( PfGenerateFontName("Helvetica", 0, 14,
Helvetica14) == NULL )
{
/* Couldn’t find the font! */
...
}
answer = PtAlert(
base_wgt, NULL, "File Not Saved", NULL,
"File has not been saved.\nSave it?",
Helvetica14, 3, btns, NULL, 1, 3,
Pt_MODAL );
Example
Now that we’ve looked at the pieces involved, it’s fairly simple to
follow the steps needed to build up the correct stem name for a given
font.
Keep these things in mind:
• Search for a font based on the foundry name (i.e. the desc member
of its FontDetails entry), not on the stem.
FontName GcaCharter14Bold;
int
fcnAppInit( int argc, char *argv[] )
{
/* Local variables */
FontDetails tsFontList [nFONTLIST_SIZE];
short sCurrFont = 0;
char caBuff[20];
if (PfQueryFonts (PHFONT_ALL_SYMBOLS,
PHFONT_ALL_FONTS, tsFontList,
nFONTLIST_SIZE) == -1)
{
perror ("PfQueryFonts() failed: ");
return (Pt_CONTINUE);
}
for (sCurrFont = 0;
sCurrFont < nFONTLIST_SIZE; sCurrFont++)
{
if ( !strcmp (tsFontList[sCurrFont].desc,
"Charter") )
break; /* we’ve found it */
}
/* Overrun check */
if (sCurrFont == nFONTLIST_SIZE)
{
/* check for a partial match */
for (sCurrFont = 0;
sCurrFont < nFONTLIST_SIZE;
sCurrFont++)
{
if ( !strncmp (tsFontList[sCurrFont].desc,
"Charter",
strlen ("Charter") ) )
break; /* found a partial match */
}
if (sCurrFont == nFONTLIST_SIZE)
{
printf ("Charter not in %d fonts checked.\n",
sCurrFont);
return (Pt_CONTINUE);
}
else
printf ("Using partial match -- ’Charter’.\n");
}
/* Is 14-point available? */
if ( !( (tsFontList[sCurrFont].losize ==
tsFontList[sCurrFont].hisize == 0)
/* proportional font -- it can be shown in
14-point*/
||
( (tsFontList[sCurrFont].losize <= 14 )
&&
(tsFontList[sCurrFont].hisize >= 14 ) ) ) )
/* 14-point fits between smallest and
largest available size */
{
printf ("Charter not available in 14-point.\n");
return (Pt_CONTINUE);
}
return( Pt_CONTINUE );
For the above code to work, you must declare the following
information in the application’s global header file. To do this, use
PhAB’s Startup Info/Modules dialog (accessed from the Application
menu).
/*********************************
*** user-defined constants ***
*********************************/
#define nFONTLIST_SIZE 100 /* an arbitrary choice of size */
/***************************
*** global variables ***
***************************/
You can avoid using a specific size for the list by calling
PfQueryFonts() with n set to 0 and list set to NULL. If you do this,
PfQueryFonts() returns the number of matching fonts but doesn’t try
to fill in the list. You can use this feature to determine the number of
items to allocate.
Remember to define this header before you start adding callbacks and
setup functions — that way, it’s automatically included as a #define.
If you forget, you’ll have to go back and add the statement manually.
For more information, see “Specifying a global header file” in the
Working with Applications chapter.
And last of all, here’s a sample callback that uses our stem name
string:
int
fcnbase_btn_showdlg_ActivateCB( PtWidget_t *widget,
ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
PtNotice (ABW_base, NULL, "Font Demonstration", NULL,
"This sentence is in 14-pt. Charter bold",
return( Pt_CONTINUE );
}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <Ap.h>
#include <Ph.h>
#include <Pt.h>
#include <errno.h>
#define FALSE 0
FontName szFont;
PtInit (NULL);
if(argc > 1)
{ if(PfGenerateFontName(argv[1], 0, 9, szFont) == NULL)
PfGenerateFontName("TextFont", 0, 9, szFont);
}
else
PfGenerateFontName("TextFont", 0, 9, szFont);
if(argc > 2)
pcText = pmbGB;
nArgs = 0;
pntPOS.x = 100;
pntPOS.y = 10;
PtSetArg(&args[nArgs], Pt_ARG_POS, &pntPOS, 0);
nArgs++;
PtSetArg(&args[nArgs], Pt_ARG_TEXT_STRING, pcText, NULL);
nArgs++;
PtSetArg(&args[nArgs], Pt_ARG_TEXT_FONT, szFont, NULL);
nArgs++;
pbtn = PtCreateWidget(PtButton, pwndMain, nArgs, args);
PtRealizeWidget(pbtn);
pntPOS.y = 100;
pntPOS.x = 75;
pntDIM.x = 300;
pntDIM.y = 300;
PtSetArg(&args[0], Pt_ARG_POS, &pntPOS, 0);
PtSetArg(&args[1], Pt_ARG_DIM, &pntDIM, 0);
PtSetArg(&args[2], Pt_ARG_RAW_DRAW_F, fnDrawCanvas, 0L);
pobjRaw = PtCreateWidget(PtRaw, pwndMain, 3, args);
PtRealizeWidget(pwndMain);
PtMainLoop ();
return(0);
}
old = PgSetStrokeColor(Pg_BLACK);
PgSetFont(szFont);
PgSetTextColor(Pg_BLACK);
PgDrawText(pcText, strlen(pcText), &pnt, 0);
pnt.x -= 10;
pnt2.x = pnt.x + tsExtent.lr.x + 20;
pnt2.y = pnt.y;
PgSetStrokeColor(Pg_BLUE);
PgDrawLine(&pnt, &pnt2);
pnt.x = 10 + rect.ul.x;
pnt.y = 100 + rect.ul.y;
PgSetStrokeColor(Pg_RED);
PgDrawIRect(tsExtent.ul.x + pnt.x,
tsExtent.ul.y + pnt.y,
(tsExtent.lr.x - min(tsExtent.ul.x, 0)
+ 1) + pnt.x, tsExtent.lr.y + pnt.y,
Pg_DRAW_STROKE);
tsExtent.lr.x /= 2;
pnt.x = 10 + rect.ul.x;
pnt.y = 150 + rect.ul.y;
PgDrawIRect(tsExtentClip.ul.x + pnt.x,
tsExtentClip.ul.y + pnt.y,
(tsExtentClip.lr.x - min(tsExtentClip.ul.x, 0)
+ 1) + pnt.x, tsExtentClip.lr.y + pnt.y,
Pg_DRAW_STROKE);
tsExtent.lr.x /= 2;
pnt.x = 10 + rect.ul.x;
pnt.y = 200 + rect.ul.y;
PgSetStrokeColor(old);
return( Pt_CONTINUE );
}
#define FALSE 0
FontName szFont;
if(argc < 2)
{ printf("Usage: pen text_string\n");
exit(EXIT_FAILURE);
}
PtInit (NULL);
ppcData = argv;
PfGenerateFontName("TextFont", 0, 9, szFont);
nArgs = 0;
pntDIM.x = 80;
pntDIM.y = 20;
PtSetArg(&args[nArgs], Pt_ARG_DIM, &pntDIM, 0);
nArgs++;
pntPOS.x = 100;
pntPOS.y = 10;
PtSetArg(&args[nArgs], Pt_ARG_POS, &pntPOS, 0);
nArgs++;
PtSetArg(&args[nArgs], Pt_ARG_TEXT_STRING, argv[1], NULL);
nArgs++;
pbtn = PtCreateWidget(PtButton, pwndMain, nArgs, args);
PtRealizeWidget(pbtn);
nArgs = 0;
pntDIM.x = 80;
pntDIM.y = 20;
PtSetArg(&args[nArgs], Pt_ARG_DIM, &pntDIM, 0);
nArgs++;
pntPOS.x = 100;
pntPOS.y = 600;
PtSetArg(&args[nArgs], Pt_ARG_POS, &pntPOS, 0);
nArgs++;
PtSetArg(&args[nArgs], Pt_ARG_TEXT_STRING, argv[1], NULL);
nArgs++;
PtSetArg(&args[nArgs], Pt_ARG_RESIZE_FLAGS,
Pt_RESIZE_XY_ALWAYS, Pt_RESIZE_XY_ALWAYS);
nArgs++;
PtSetArg(&args[nArgs], Pt_ARG_BORDER_WIDTH, 0L, 0L);
nArgs++;
PtSetArg(&args[nArgs], Pt_ARG_MARGIN_LEFT, 0L, 0L);
nArgs++;
PtSetArg(&args[nArgs], Pt_ARG_MARGIN_RIGHT, 0L, 0L);
nArgs++;
pobjLabel = PtCreateWidget(PtLabel, pwndMain, nArgs, args);
PtRealizeWidget(pobjLabel);
pntPOS.y = 100;
pntPOS.x = 75;
pntDIM.x = __WIN_SIZE_X_ - 75 - 10;
pntDIM.y = 300;
PtSetArg(&args[0], Pt_ARG_POS, &pntPOS, 0);
PtSetArg(&args[1], Pt_ARG_DIM, &pntDIM, 0);
PtSetArg(&args[2], Pt_ARG_RAW_DRAW_F, fnDrawCanvas, 0L);
(void) PtRealizeWidget(pwndMain);
PtMainLoop ();
return(0);
}
pucFont = szFont;
pc = argv[1];
piIndx = (int *)calloc(50, sizeof(int));
piPos = (int *)calloc(50, sizeof(int));
if(strlen(pc) < 4)
{ printf("Pick a longer string, must be at least\
4 characters.\n");
exit(EXIT_SUCCESS);
}
old = PgSetStrokeColor(Pg_BLACK);
PgSetFont(pucFont);
PgSetTextColor(Pg_BLACK);
PgDrawIRect(tsExtent.ul.x + pnt.x,
tsExtent.ul.y + pnt.y,
(tsExtent.lr.x - min(tsExtent.ul.x, 0) + 1) +
pnt.x, tsExtent.lr.y + pnt.y, Pg_DRAW_STROKE);
PgDrawIRect(tsExtent.ul.x + pnt.x,
tsExtent.ul.y + pnt.y,
(tsExtent.lr.x - min(tsExtent.ul.x, 0) + 1) +
pnt.x, tsExtent.lr.y + pnt.y, Pg_DRAW_STROKE);
PgSetFont(pucFont);
PgSetTextColor(Pg_BLACK);
PgDrawText(pc, strlen(pc), &pnt, 0);
default: piIndx[0] = n;
PfExtentTextCharPositions(&tsExtent,
&tsPos, pc, pucFont, piIndx, piPos,
1, 0L, 0, 0, NULL);
printf("Position: %d\n", piPos[0]);
pnt.x = 10 + rect.ul.x + piPos[0];
PgDrawText(pc + n, strlen(pc) - n, &pnt, 0);
PgFlush();
sleep(1);
break;
}
}
/* End draw string, then overlay individual characters
on top from right to left. */
PgSetStrokeColor(old);
free(piPos);
free(piIndx);
return( Pt_CONTINUE );
}
In this chapter. . .
Print contexts 608
Starting a print job 609
Printing the desired widgets 612
Suspending and resuming a print job 615
Ending a print job 615
Freeing the print context 616
Example 616
• to a print context (or PC) for printing. See “Print Contexts,” below.
To print in Photon:
Print contexts
A print context is a PpPrintContext_t structure whose members
control how printing is to be done. For information about what’s in a
print context, see the Photon Library Reference.
pc = PpCreatePC();
• If you want a widget to fill the page, set the source size to equal the
widget’s dimensions. You can call PpSetCanvas() to do this.
• by default, the source resolution is 100 pixels per inch so that fonts
are printed at an appealing size. You can get the size of the interior
canvas by calling PpGetCanvas(), which gives dimensions that
take into account the marginal, nonprintable area.
When setting the source size, take the nonprintable area of the printer
into account. All printers have a margin around the page that they
won’t print on, even if the page margins are set to 0. Therefore, the
size set above is actually a bit larger than the size of a page, and the
font will be scaled down to fit on the printable part of the page.
In the following example, the page size and nonprintable area are
taken into account to give the proper source size and text height. Try
this, and measure the output to prove the font is 1″ high from ascender
to descender:
#include <stdio.h>
#include <stdlib.h>
#include <Pt.h>
{quit_cb, NULL} };
if (PtInit(NULL) == -1)
PtExit(EXIT_FAILURE);
PtRealizeWidget(window);
PtMainLoop();
return (EXIT_SUCCESS);
}
You should also set the source offset, the upper left corner of what’s
to be printed. For example, if you have a button drawn at (20, 20)
from the top left of a pane and you want it to be drawn at (0, 0) on the
page, set the source offset to (20, 20). Any other widgets are drawn
Once the source size and offset have been set, you can start printing:
PpStartJob(pc);
PpContinueJob(pc);
• Pg* functions
If you want to print all the contents of a widget that scrolls, you’ll
need to do some special preparations. See “Printing scrolling
widgets” below.
PpPrintNewPage(pc);
Note that once you call PpStartJob(), any changes to the print context
take effect after the next call to PpPrintNewPage().
Photon assumes that the page numbers increase by one. If this isn’t
the case, manually set the Pp_PC_PAGE_NUM member of the print
context to the correct page number. Don’t make the page number
decrease because the print drivers might not work properly.
PtList
The only way to make a PtList print (or draw) all the items is by
resizing it to be the total height of all the items. The easiest way is
probably by using the resize policy:
This will work only if the total height is smaller than 65K pixels.
PtMultiText
To print a PtMultiText widget’s entire text, breaking the output into
pages:
2 Get the printer settings for printing: the orientation, page size,
and the margins.
3 Adjust the printer settings for what you want and then use
PpSetPC() to set them.
7 Go through the user’s multitext and get the attributes for each
line (color, font, tabs, etc) and set the print multitext widget’s
attributes accordingly.
8 Once you’ve set all of the attributes to match, specify the top
line of the print multitext widget. This positions the widget to
start printing.
9 Get the number of lines that are completely visible in the print
multitext widget, as well as the total number of lines.
11 Delete the lines that you just printed from the print multitext
widget. Doing this causes the next group of lines that you want
to print to become the visible lines of the widget.
PtScrollArea
For a PtScrollArea, you need to print its virtual canvas, which is
where all widgets created within or moved to a scroll area are placed:
2 Get the area (Pt_ARG_AREA) of the virtual canvas, and use its
size member as the source size in the print context.
PpSuspendJob( pc );
PpContinueJob( pc );
PpSuspendJob(pc);
PpEndJob(pc);
You can reuse the print context for new print jobs, eliminating the
need to create and initialize it again.
Example
This example creates an application with a main window, and a pane
with a few widgets on it. When you press the Print button, a Print
Selection Dialog appears. When you select this dialog’s Print or
Preview button, the pane is “drawn” on the printer.
#include <stdio.h>
#include <stdlib.h>
#include <Pt.h>
PpReleasePC (pc);
exit (EXIT_SUCCESS);
return (Pt_CONTINUE);
}
return (Pt_CONTINUE);
}
if (PtInit(NULL) == -1)
PtExit(EXIT_FAILURE);
PtRealizeWidget(window);
PtMainLoop();
return (EXIT_SUCCESS);
}
In this chapter. . .
Transport mechanism 623
Using drag and drop 624
Registering new transport types 634
Drag and drop lets you drag arbitrary data within an application or
between applications.
Transport mechanism
Photon’s transport mechanism lets you transfer arbitrary data from
one application to another, even if the applications are on different
platforms with different endian-ness. This mechanism is used as the
basis for drag and drop, but could be used for other purposes such as
configuration files.
There are two ways to transport data:
• string
• raw
• PhDim
• PhArea
• PhPoint
• PhImage
Data
When the data arrives at the destination, the headers are extracted to
get the unpacking instructions for the data. The transport mechanism
automatically unpacks the data; the application gets the data in its
original form.
The basic steps (described in more detail in the sections that follow)
are:
1 The user holds down the pointer button on the widget that’s to
be the source of the drag-and-drop operation.
2 In its Pt_CB_OUTBOUND callback, the source widget packs
up the data to be dragged, and starts a drag-and-drop operation.
3 The user drags the data and decides to drop it on a widget.
4 In its Pt_CB_DND callback, the destination widget decides
which pieces of dragged data (if any) it will accept. Any data
that’s accepted is unpacked automatically. The data is stored in
allocated memory; the destination should free the memory
when it doesn’t need the data any more.
Example
Here’s an example of a callback that initiates a drag-and-drop
operation for a PtLabel widget. You can use this callback for the
label widget’s Pt_CB_OUTBOUND callback.
The callback assigns the same grouping number to the image and the
alternate text, to indicate that they’re different forms of the same data.
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "abimport.h"
#include "proto.h"
#define TEXT_GROUP 0
#define IMAGE_GROUP 1
PtTransportReqDataCB_t request_callback;
if ((*label_type == Pt_Z_STRING) ||
(*label_type == Pt_TEXT_IMAGE))
{
/* Get the widget’s text and pack it inline. */
PtGetResource( widget, Pt_ARG_TEXT_STRING,
&widget_text, 0);
PtTransportType( tctrl, "text", "plain",
TEXT_GROUP, Ph_TRANSPORT_INLINE, "string",
widget_text, 0, 0);
}
if ((*label_type == Pt_IMAGE) ||
(*label_type == Pt_TEXT_IMAGE))
{
PtGetResource( widget, Pt_ARG_LABEL_IMAGE,
&image, 0);
if (image)
{
req = PtTransportRequestable ( tctrl,
"image", "an image", IMAGE_GROUP,
Ph_TRANSPORT_INLINE, "PhImage",
NULL, NULL );
PtAddResponseType( tctrl, req, "image",
"an image", Ph_TRANSPORT_INLINE,
"PhImage", image, 0, 0);
}
}
Source widget
The source widget of a drag-and-drop operation can receive events
that describe the status of the operation. If you don’t want these
events, set Pt_DND_SILENT in the flags argument to PtInitDnd().
Ph_EV_DND_INIT
The operation has started successfully.
Ph_EV_DND_CANCEL
The operation was canceled (for example, if the drop occurred
when not over a drop zone, or the destination terminated the
operation before receiving the drop or before it finished fetching
requestable data).
If the operation is canceled in this way, the library cleans up the
data structures automatically.
Ph_EV_DND_COMPLETE
The drag-and-drop event is enqueued at the destination (the
destination hasn’t seen it yet).
Ph_EV_DND_DELIVERED
The destination has dequeued the drag-and-drop event.
Destination widget
The subtypes of a drag-and-drop event that are of interest to the
destination of the operation are:
Ph_EV_DND_ENTER
Someone has dragged some data into the widget’s region but
hasn’t yet released it. This is the reason_subtype the first time
that the drag-and-drop callback is called.
At this time, your application decides if it will accept the data to
be dropped. It must build an array of PtDndFetch_t structures
Ph_EV_DND_MOTION
The pointer is moving inside the widget’s region. This type of
event is emitted only if the Pt_DND_SELECT_MOTION bit is
set in the select_flags member of the PtDndFetch_t structure
for a piece of selected data.
Ph_EV_DND_DROP
The user has dropped the data.
For this reason_subtype, the callback should retrieve the
selected data from the event. This might involve some
automatic, nonblocking communication with the source of the
data — to prevent any communication with the source, specify
Ph_TRANSPORT_INLINE as the only acceptable transport
protocol.
If the drop is successful, the memory used by the transport
mechanism is automatically freed.
Ph_EV_DND_LEAVE
The pointer has moved out of the widget’s region, but the user
didn’t drop the data.
Here’s an example that works with the callback given above for a
PtLabel widget. This callback accepts the following from the
drag-and-drop data:
• text
• an image
The source widget packed the image and the alternate text as
requestable data, but the destination doesn’t have to do anything to
request it; the transport mechanism does it automatically.
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "abimport.h"
#include "proto.h"
enum {
PLAIN_TEXT = 0,
IMAGE,
IMAGE_TEXT,
};
{
PtDndCallbackInfo_t *dndcb = cbinfo->cbdata;
int deep_free = 1, num_matches;
cbinfo = cbinfo;
switch (cbinfo->reason_subtype)
{
case Ph_EV_DND_ENTER:
num_matches = PtDndSelect (widget,
stuff_i_want,
sizeof( stuff_i_want ) / sizeof( stuff_i_want[0] ),
NULL, NULL, cbinfo );
break;
case Ph_EV_DND_DROP:
switch (dndcb->fetch_index)
{
case PLAIN_TEXT:
PtSetResource (widget, Pt_ARG_TEXT_STRING,
dndcb->data, strlen(dndcb->data));
break;
case IMAGE:
PtSetResource (widget, Pt_ARG_LABEL_IMAGE,
dndcb->data, 0);
free (dndcb->data);
deep_free = 0;
break;
case IMAGE_TEXT:
printf (
"No image; the alternate text is: %s\n",
(char *)dndcb->data);
break;
}
if (deep_free) {
PhFreeTransportType (dndcb->data,
dndcb->trans_hdr->packing_type);
}
break;
return( Pt_CONTINUE );
}
• a list of members within the type that reference data outside the
base size of the type in question (reference or pointer-type
members)
The source and destination applications must both define the data type
in their transport registries before the data can be successfully
transported.
This structure could easily be packed using the raw type because it
doesn’t make any external references (i.e. it has no pointer members).
But that doesn’t protect the transported data from endian differences
between the source and destination. So even for this simple structure,
a type description detailing its endian sensitivity is beneficial.
The type definition starts with an array of int unsigned entries that
described the endian-sensitivity for each member:
static const int unsigned Simp1Endians[] = {
Tr_ENDIAN( Simp1_t, num ),
Tr_ENDIAN_ARRAY( Simp1_t, nums_10 ),
Tr_ENDIAN_ARRAY( Simp1_t, vals_5 ),
0 /* End of the endian list */
};
Note that this list must end with an entry of 0. The name member isn’t
endian-sensitive, so it isn’t included in the list.
All types or references to types correct the endian-ness of their
members based on the endian array defined for the type. The
classifications of endian-sensitive members are:
Having defined the endian list for our simple data type, let’s create the
definition to go into the transport registry:
static const PhTransportRegEntry_t Simp1TransDef = {
"simp1",
Ph_PACK_STRUCT,
sizeof( Simp1_t ),
0,
NULL,
&Simp1Endians,
NULL
};
This new type, simp1, can now be used with any of the transport
functions to pack or unpack data.
The destination application doesn’t need to concern itself with the
endian orientation of the source. When the destination unpacks this
type, the transport mechanism automatically corrects the endian-ness
using the endian definition in the registered transport type. This is
particularly beneficial in a multiplatform, networked environment. If
the transport mechanism is used to write binary configuration files,
the same files can be used by applications regardless of the endian
orientation of the machine they are running on.
/* Scalar array */
int nums_10[10];
/* Reference to a string */
char *last_name2;
/* Scalar array */
short vals_5[5];
} Simp2_t;
Clear-references list
Here’s the clear_refs list for this structure:
Endian list
Here’s the endian list for this structure:
Here’s the full list of endian manifests for each type of member:
Reference (char)
None
Reference (short)
Tr_ENDIAN_REF( type, member )
short *short_nums;
Tr_ENDIAN_REF( Sample_t, short_nums )
int *long_nums;
Tr_ENDIAN_REF( Sample_t, long_nums )
Fixup list
The Simp2_t structure given earlier includes some entries that
reference data outside the structure. These elements need
PhTransportFixupRec_t entries in the fixup list to tell the
transport mechanism how to get the data:
Scalar None.
Reference (string)
Tr_STRING( type, member )
Registered type
Tr_TYPE( type, member, type_name )
int num_nums;
int *int_array;
Tr_REF_ARRAY( Sample_t, int_array, \
Tr_FETCH( Sample_t, num_nums) )
Simp1_t simple_instance
Tr_TYPE( Sample_t, simple_instance, "simp1" )
Simp1_t simple_array[5]
Tr_TYPE_ARRAY( Sample_t, simple_array, "simp1" )
Simp1_t *simp1_ref
Tr_REF_TYPE( Sample_t, simp1_ref, "simp1" )
short num_simp1s;
Simp1_t *simp1_ref
Tr_REF_TYPE_ARRAY( Sample_t, simp1_ref, \
Tr_FETCH( Sample_t, num_simp1s ), "simp1" )
short num_simp1s;
Simp1_t **simp1_ref ;
Tr_REF_TYPE_REF_ARRAY( Sample_t, simp1_ref, \
Tr_FETCH( Sample_t, num_simp1s ), "simp1" )
Registry entry
Finally, here’s the registry entry for Simp2_t:
static const PhTransportRegEntry_t
Simp2TransDef = {
"simp2",
Ph_PACK_STRUCT,
sizeof( Simp2_t ),
sizeof(Simp2Fixups)/sizeof(Simp2Fixups[0]),
&Simp2Fixups,
&Simp2Endians,
&Simp2ClearRefs
};
Transport functions
This section describes the low-level functions and data types that deal
with the transport mechanism. Some functions are called by the
application that’s the source of the data, some are called by the
destination, and some are called by both.
Both applications
Both applications use these:
PhTransportRegEntry_t
Data structure that describes data to be transported
PhRegisterTransportType()
Add a new transport type to the transport registry
PhFindTransportType()
Find a transport type in the transport registry
Source application
The source application uses these, in roughly this order:
PhTransportCtrl_t
Control structure for the Photon transport mechanism
PhCreateTransportCtrl()
Allocate a PhCreateTransportCtrl() structure
PhTransportType()
Pack data into a PhTransportCtrl_t structure
PhTransportFindLink()
Search a linked list of transport data for some specific data
PhTransportLink_t
Entry in a linked list of transport data
PhLinkTransportData()
Add transport data to a linked list
PhGetNextInlineData()
Get the data for the next entry in a linked list of transport data
PhGetTransportVectors()
Build an I/O vector of data to be transported
PhFreeTransportType()
Free data associated with a transport registry entry
PhReleaseTransportCtrl()
Free a PhTransportCtrl_t structure
These are low-level functions that you’ll probably never need to call
directly:
PhAllocPackType()
Allocate a buffer and pack transport data into it
PhPackEntry() Pack transport data, given a transport registry
entry
PhPackType() Pack transport data, given the type of data
Destination application
The destination application uses these, in roughly this order:
PhGetAllTransportHdrs()
Extract all the headers from a buffer of packed
transport data
PhGetTransportHdr()
Extract the header from a buffer of packed transport
data
PhGetNextTransportHdr()
Get the next header from a buffer of packed transport
data
PhLocateTransHdr()
Look for specific data in a linked list of transport
headers
PhMallocUnpack()
Unpack transport data, using a custom
memory-allocation function
PhUnlinkTransportHdr()
Remove an entry from a linked list of transport
headers
PhReleaseTransportHdrs()
Free a linked list of headers for packed transport data
In this chapter. . .
Photon coordinate space 649
Region coordinates 650
Regions and event clipping 653
Placement and hierarchy 654
Using regions 660
System information 662
You can use phview to see what regions exist on your machine. For
more information, see the Utilities Reference.
Upper-left Upper-right
quadrant quadrant
(0, 0)
VGA
display
(640, 480)
Lower-left Lower-right
quadrant quadrant
The root region has the same dimensions as the entire coordinate
space. As a rule, graphics drivers map the display screen to the
location shown in the above diagram and place the Photon origin at
Region coordinates
Region origins
When an application specifies coordinates within a given region, these
are relative to the region’s origin. The application specifies this origin
when it opens the region.
Root region
Child's origin
(0, 0)
(100, 100)
Parent's origin
Root region
Child's origin
(-50, -50)
(50, 50)
Parent's origin
Region's origin
Widget's canvas
Widget's region
Root region
Child's origin
(0, 0)
Parent's origin
• Child 2 can emit or collect events only in the smaller gray area that
overlaps with its parent region
Child region 1
Parent region 2
Child region 2
Root region
Parent region
The Photon Manager always places child regions in front (i.e. on the
user side) of their parents:
Root region
Parent region
Child region
Brother regions
Besides having a parent, a region may have “brothers,” i.e. other
regions who have the same parent. A region knows about only two of
its brothers — the one immediately in front and the one immediately
behind.
The following diagram shows a parent with three children, and the
relationship that child region 2 has with its brothers:
Root region
Parent region
Child region 2
Brother in front
When the application opens a region (e.g. child region 2 in the above
diagram), it can specify neither, one, or both immediate brothers.
Depending on how the application specifies these brothers, the new
region may be placed according to default rules (see below) or at a
specific location.
Default placement
If an application opens a region without specifying brothers, the
Photon Manager places that region using default placement rules. In
most cases, these rules cause a newly opened region to be placed in
front of its frontmost brother, which then becomes “brother behind”
of the new region. (To use different placement rules, you can specify
the Ph_FORCE_FRONT flag.)
Root region
Parent region
Child region 1
Root region
Parent region
Child region 1
Child region 2
Ph_FORCE_FRONT flag
Root region
Parent region
Child region 1
(forced to front)
Root region
Parent region
Child region 2
Child region 1
(forced to front)
Root region
Parent region
Child region 2
Child region 3
Child region 1
(forced to front)
Specific placement
In contrast to default placement, if any brother is specified when a
region is opened, then that specification controls the placement of the
new region. We refer to this as specific placement.
If a “behind” brother is specified, then the newly opened region
automatically is placed in front of that brother.
If an “in front” brother is specified, then the newly opened region is
automatically placed behind that brother.
Using regions
Opening a region
To open a region, create a PtRegion widget. The PtRegion widget
isn’t included in PhAB’s widget palette; to instantiate it:
• Create a window module, select it, and use the Change Class item
in PhAB’s Edit menu to turn the window into a PtRegion. For
more information, see “Changing a widget’s class” in the chapter
on Creating Widgets in PhAB.
Placing regions
While a region is always in front of its parent, the region’s placement
relative to its brothers is flexible. See “Placement and hierarchy” for
more information about “default” and “specific” placement.
The PhRegion_t structure (see the Library Reference) contains the
following members. These indicate the relationship of a region with
its siblings:
Specifying brothers
System information
You can get the following information about your system:
You don’t get information about each region. Instead, you get the
minimum value of each type of information.
For example, if several graphics-driver regions overlapping your
window have different bandwidths, the bandwidth given is the
minimum of them.
There are two functions that you can use to get system information:
PhQuerySystemInfo()
Get the information for a given region.
PtQuerySystemInfo()
Get the information for a widget (usually a window).
In this chapter. . .
Pointer events 667
Emitting events 670
Event coordinates 674
Event handlers — raw and filter callbacks 674
Collecting events 678
Event compression 678
Dragging 679
The interactions between applications, users and the Photon server are
represented by data structures called events.
Event information is stored in structures of type PhEvent_t; see the
Photon Library Reference.
Pointer events
Most of the time, you can use a widget’s callbacks to handle what the
user does while pointing to it. If you’re working with event handlers,
you’ll need to know what events Photon emits.
Pressing a button
When you press the pointer button, Photon emits a
Ph_EV_BUT_PRESS event to the widget that currently has focus.
Releasing a button
When you release the button, Photon emits two
Ph_EV_BUT_RELEASE events:
Multiple clicks
Whenever you press or release the mouse button, the event includes
the click count. How can your application determine that you clicked,
instead of double clicked?
There’s a click counter in the event data that’s associated with
Ph_EV_BUT_PRESS and Ph_EV_BUT_RELEASE events; to get this
data, call PhGetData(). The data for these events is a structure of type
PhPointerEvent_t (see the Photon Library Reference for details);
its click_count member gives the number of times that you clicked the
mouse button.
If you keep clicking quickly enough without moving the mouse, the
counter keeps incrementing. If you move the mouse or stop clicking
for a while, the counter resets and Photon emits a
Ph_EV_BUT_RELEASE event with a subtype of
Ph_EV_RELEASE_ENDCLICK.
In other words, the first click generates a Ph_EV_BUT_PRESS event
and a pair of Ph_EV_BUT_RELEASE events (one REAL and one
PHANTOM) with click_count set to 1. Then, depending on whether
the user clicks again soon enough or not, you get either:
After the second click, you either get a third one or an ENDCLICK,
and so on. But eventually you get an ENDCLICK — and the next
time the person clicks, the click count is 1 again.
Modifier keys
If you need to determine what keys were pressed in a pointer event,
call PhGetData() to get the event data that’s included for
Ph_EV_BUT_PRESS and Ph_EV_BUT_RELEASE events. The data for
these events is a structure of type PhPointerEvent_t (described in
the Photon Library Reference); check its key_mods member to
determine the modifier keys that were pressed.
For example, this Pt_CB_ACTIVATE callback lists the modifier keys
that were pressed when the pointer button was released:
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "abimport.h"
#include "proto.h"
int
check_keys( PtWidget_t *widget, ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
PhPointerEvent_t *event_data;
if (cbinfo->event->type != Ph_EV_BUT_RELEASE) {
printf ("Not a Ph_EV_BUT_RELEASE event\n");
} else {
printf ("It’s a Ph_EV_BUT_RELEASE event\n");
event_data = (PhPointerEvent_t *)
PhGetData (cbinfo->event);
}
return( Pt_CONTINUE );
}
Emitting events
The most general way for your application to emit an event is to call
PhEmit():
int PhEmit( PhEvent_t *event,
PhRect_t *rects,
void *data );
data Valid data for the type of event being emitted. Each type of
event has its own type of data, as described for the
PhEvent_t structure in the Photon Library Reference.
If the event-specific data isn’t in contiguous memory, you
may find PhEmitmx() more useful than PhEmit():
A nonnegative value
Successful completion.
Inclusive event
For an inclusive event, do the following:
Direct event
For a direct event, do the following:
• The widget still has focus — there might be other events enqueued
before yours.
• If you know which region you want to send the event to, emit a
Ph_EV_KEY event directly to that region:
event->collector.rid = rid;
event->flags |= Ph_EVENT_DIRECT;
PhEvent_t event;
PhKeyEvent_t key_event;
PhRect_t rect;
rect.ul.x = rect.ul.y = 0;
rect.lr.x = rect.lr.y = 0;
event.type = Ph_EV_KEY;
event.emitter.rid = Ph_DEV_RID;
event.num_rects = 1;
event.data_len = sizeof(key_event);
event.input_group = 1;
key_event.key_cap = key;
key_event.key_sym = key;
return;
}
Event coordinates
When an event is emitted, the coordinates of its rectangle set are
relative to the emitting region’s origin. But when the event is
collected, its coordinates become relative to the collecting region’s
origin.
The Photon Manager ensures this happens by translating coordinates
accordingly. The translation member of the PhEvent_t specifies the
translation between the emitting region’s origin and the collecting
region’s origin.
Pt_CB_FILTER
Invoked before the event is processed by the
widget. They let you perform actions based on the
event before the widget sees it. They also give you
the opportunity to decide if the event should be
ignored, discarded, or allowed to be processed by
the widget.
These callbacks are called every time a Photon event that matches an
event mask (provided by the application) is received. Since all the
widget classes in the Photon widget library are descended from the
PtWidget, these callbacks can be used with any widget from the
Photon widget library.
Pt_CONSUME
The event is consumed and no other raw callbacks are
invoked as the event is passed up to the widget’s parent.
Pt_CONTINUE
The event is passed up to the widget’s parent.
Let’s look at a simple widget family to see how this works. Let’s
suppose you have a window that contains a pane that contains a
button. Here’s what normally happens when you click on the button:
Collecting events
Most applications collect events by calling PtMainLoop(). This
routine processes Photon events and supports work procedures and
input handling.
If your application doesn’t use widgets, you can collect events:
Event compression
The Photon Manager compresses drag, boundary, and pointer events.
That is, if an event of that type is pending when another event arrives,
the new event will be merged with the unprocessed events. As a
result, an application sees only the latest values for these events and is
saved from collecting too many unnecessary events.
Dragging
If you need to capture mouse coordinates, for example to drag
graphical objects in your application, you’ll need to work with events.
outline dragging
The user sees an outline while dragging. When the dragging is
complete, the application repositions the widget.
opaque dragging
The application moves the widget as the dragging progresses.
Initiating dragging
Where you initiate the dragging depends on how the user is meant to
drag widgets. For example, if the user holds down the left mouse
button on a widget to drag it, initiate dragging in the widget’s Arm
(Pt_CB_ARM) or Outbound (Pt_CB_OUTBOUND) callback. Make
sure that Pt_SELECTABLE is set in the widget’s Pt_ARG_FLAGS
resource.
Dragging is started by calling the PhInitDrag() function:
int PhInitDrag( PhRid_t rid,
unsigned flags,
PhRect_t *rect,
PhRect_t *boundary,
unsigned int input_group,
PhDim_t *min,
PhDim_t *max,
const PhDim_t *step,
const PhPoint_t *ptrpos,
const PhCursorDescription_t *cursor );
• Ph_TRACK_LEFT
• Ph_TRACK_RIGHT
• Ph_TRACK_TOP
• Ph_TRACK_BOTTOM
Outline dragging
The following example shows an Arm (Pt_CB_ARM) callback that
initiates outline dragging:
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "globals.h"
#include "abimport.h"
#include "proto.h"
int
start_dragging( PtWidget_t *widget,
ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
PhDim_t *dimension;
PhRect_t rect;
PhRect_t boundary;
dragged_widget = widget;
return( Pt_CONTINUE );
}
Opaque dragging
If you want to use opaque dragging, add the Ph_DRAG_TRACK flag
to the call to PhInitDrag():
PhInitDrag( PtWidgetRid (ABW_base),
Ph_TRACK_DRAG | Ph_DRAG_TRACK,
&rect, &boundary,
PhInputGroup( cbinfo->event ),
NULL, NULL, NULL, NULL, NULL );
The raw or filter callback must be defined for the widget whose region
was passed to PhInitDrag(), not for the widget being dragged. For the
example given, the Raw callback is defined for the base window.
Ph_EV_DRAG_START
The user has started to drag.
Ph_EV_DRAG_MOVE
The dragging is in progress (opaque dragging only).
Ph_EV_DRAG_COMPLETE
The user has released the mouse button.
Outline dragging
If you’re doing outline dragging, the event subtype you’re interested
in is Ph_EV_DRAG_COMPLETE. When this event occurs, your
callback should:
For example, here’s the Raw callback for the outline dragging
initiated above:
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "globals.h"
#include "abimport.h"
#include "proto.h"
int
end_dragging( PtWidget_t *widget,
ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
PhDragEvent_t *dragData;
PhPoint_t new_pos;
if (cbinfo->event->subtype != Ph_EV_DRAG_COMPLETE)
{
return (Pt_CONTINUE);
}
new_pos.x = dragData->rect.ul.x
+ cbinfo->event->translation.x;
new_pos.y = dragData->rect.ul.y
+ cbinfo->event->translation.y;
printf ("New position: (%d, %d)\n", new_pos.x, new_pos.y);
return( Pt_CONTINUE );
Opaque dragging
The callback for opaque dragging is similar to that for outline
dragging—the only difference is the subtype of event handled:
if (cbinfo->event->subtype != Ph_EV_DRAG_MOVE)
{
return (Pt_CONTINUE);
}
In this chapter. . .
Window-management flags 689
Notification callback 694
Getting and setting the window state 696
Managing multiple windows 699
Window-manager functions 699
Running a standalone application 700
Modal dialogs 700
Window-management flags
The PtWindow widget defines various types of flags:
Pt_ARG_WINDOW_RENDER_FLAGS
Which window decorations appear in the window frame.
Pt_ARG_WINDOW_MANAGED_FLAGS
How the Window Manager operates on the window.
Pt_ARG_WINDOW_NOTIFY_FLAGS
Which Window Manager events your application would like to
be notified of.
Pt_ARG_WINDOW_STATE
The current state of the window.
If you change the state of the window after it’s realized, you’ll need to
let the Window Manager know. See “Getting and setting the window
state” later in this chapter.
Window-rendering flags
The Pt_ARG_WINDOW_RENDER_FLAGS resource specifies what
appears in the window’s frame.
In PhAB, if you turn these flags off, PhAB provides a border, title,
and buttons that allow you to re-size, move, minimize, and close the
window in design mode. The flags still affect how the window
appears when the application is running.
Window-managed flags
The Pt_ARG_WINDOW_MANAGED_FLAGS resource specifies
what operations you want the window manager to handle:
continued. . .
Window-notify flags
The Pt_ARG_WINDOW_NOTIFY_FLAGS resource specifies which
window-manager operations your application should be notified of.
This resource uses the same bits as
Pt_ARG_WINDOW_MANAGED_FLAGS:
continued. . .
Notification callback
When a window manager operation occurs that’s listed in the
window’s notify flags (Pt_ARG_WINDOW_NOTIFY_FLAGS), the
window’s Pt_CB_WINDOW callback is invoked.
Each callback function listed in this resource is passed a
PtCallbackInfo_t structure (see the Photon Widget Reference)
that contains at least the following members:
reason Pt_CB_WINDOW
reason_subtype
0 (not used).
{
PhWindowEvent_t *we = cbinfo->cbdata;
char *btns[] = { "&Yes", "&No" };
char Helvetica14[MAX_FONT_TAG];
if ( we->event_f == Ph_WM_CLOSE ) {
case 1: /* yes */
PtExit (EXIT_SUCCESS);
break;
case 2: /* no */
return (Pt_CONTINUE);
}
} else {
/* Check for other events. */
}
return( Pt_CONTINUE );
continued. . .
You can get and set the state of the window at any time by using the
Pt_ARG_WINDOW_STATE resource, but you might get unexpected
results if the user is changing the window state at the same time.
The safest time to use this resource to set the window state is before
the window is realized. For example, you could set it when creating
the PtWindow widget or in the window’s
Pt_CB_WINDOW_OPENING callback. The setting will be in effect
when the window is realized.
You can set Pt_ARG_WINDOW_STATE after the window has been
realized, basing your changes on what you think the current window
state is, but it’s safer to tell the window manager how you want to
change the state, by calling:
PtForwardWindowEvent()
Change the state for the window associated with a given region
ID
PtForwardWindowTaskEvent()
Change the state for a window associated with a given Photon
connection ID
PhWindowEvent_t event;
return id;
}
Once you have the connection ID, you can minimize an open window
that belongs to the other application with this code:
PhWindowEvent_t event;
When you call these functions, you’re asking the window manager to
do the specified action. If the action isn’t set in the managed flags
(Pt_ARG_WINDOW_MANAGED_FLAGS) for the given window, the
window manager doesn’t do it.
Window-manager functions
The following low-level functions are associated with the window
manager, but you shouldn’t use them in an application that uses
widgets:
PhWindowChange()
Modify the attributes of a window’s region
PhWindowClose()
Close a window
PhWindowOpen()
Create a window region
PhWindowQueryVisible()
Query a visible extent
PtConsoleSwitch()
Switch to another virtual console
PtForwardWindowEvent()
Forward a window event
PtForwardWindowTaskEvent()
Forward a window event to a task
PtWindowConsoleSwitch()
Switch to the console a given window’s displayed on
PtWindowGetFrameSize()
Determine the size of a window’s frame
Modal dialogs
Sometimes, you want your program to prompt the user for
information before continuing. You usually do this by popping up a
dialog; if you don’t want the user to be able to select any other
operations before providing the information, you should use a modal
dialog.
A modal dialog doesn’t allow user input to go to any of the other
widgets in the application. To use a modal dialog to prompt for
information, you have to make sure that events are processed within
the callback function.
PtBlockAllWindows()
Block all windows except the one with a given widget
PtBlockWindow()
Block a given window
call = call;
if (!closure->done) {
PtAppRemoveWorkProc(NULL, closure->work_id);
}
PtDestroyWidget(closure->dialog->widget);
free(closure->dialog);
return (Pt_CONTINUE);
}
w = w; call = call;
dialog = create_working_dialog(parent);
if (dialog)
{
CountdownClosure *closure =
(CountdownClosure *)
malloc(sizeof(CountdownClosure));
if (closure)
{
PtWorkProcId_t *id;
closure->dialog = dialog;
closure->value = 0;
closure->maxvalue = 200000;
closure->done = 0;
closure->work_id = id =
PtAppAddWorkProc(NULL, count_cb, closure);
PtAddCallback(dialog->ok_button, Pt_CB_ACTIVATE,
done, closure);
PtRealizeWidget(dialog->widget);
PtUnblockWindows (blocked_list);
}
}
return (Pt_CONTINUE);
}
#include <stdlib.h>
#include <Pt.h>
/* New member: */
PtModalCtrl_t modal_control;
} CountdownClosure;
if (dialog)
{
nargs = 0;
PtSetArg(&args[nargs], Pt_ARG_WIN_PARENT, parent, 0);
nargs++;
PtSetParentWidget(NULL);
dialog->widget = window =
PtCreateWidget(PtWindow, parent, nargs, args);
nargs = 0;
PtSetArg(&args[nargs], Pt_ARG_GROUP_ORIENTATION,
Pt_GROUP_VERTICAL, 0); nargs++;
PtSetArg(&args[nargs], Pt_ARG_GROUP_VERT_ALIGN,
Pt_GROUP_VERT_CENTER, 0); nargs++;
group = PtCreateWidget(PtGroup, window, nargs, args);
nargs = 0;
dim.w = 200;
dim.h = 100;
PtSetArg(&args[nargs], Pt_ARG_DIM, &dim, 0); nargs++;
PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING,
"Counter: ", 0); nargs++;
dialog->label = PtCreateWidget(PtLabel, group,
nargs, args);
nargs = 0;
PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING, "Stop", 0);
nargs++;
dialog->ok_button = PtCreateWidget(PtButton, group,
1, args);
}
return dialog;
}
PtCallbackInfo_t *call)
{
CountdownClosure *closure =
(CountdownClosure *)client;
call = call;
if (!closure->done) {
PtAppRemoveWorkProc(NULL, closure->work_id);
}
PtDestroyWidget(closure->dialog->widget);
free(closure->dialog);
return (Pt_CONTINUE);
}
int
count_cb(void *data)
{
CountdownClosure *closure =
(CountdownClosure *)data;
char buf[64];
int finished = 0;
if ( closure->value++ == 0 || closure->value %
1000 == 0 )
{
sprintf(buf, "Counter: %d", closure->value);
PtSetResource( closure->dialog->label,
Pt_ARG_TEXT_STRING, buf, 0);
}
if ( closure->value == closure->maxvalue )
{
closure->done = finished = 1;
PtSetResource( closure->dialog->ok_button,
Pt_ARG_TEXT_STRING, "Done", 0);
}
w = w; call = call;
dialog = create_working_dialog(parent);
if (dialog)
{
CountdownClosure *closure =
(CountdownClosure *)
malloc(sizeof(CountdownClosure));
if (closure)
{
PtWorkProcId_t *id;
closure->dialog = dialog;
closure->value = 0;
closure->maxvalue = 200000;
closure->done = 0;
closure->work_id = id =
PtAppAddWorkProc(NULL, count_cb, closure);
PtAddCallback(dialog->ok_button, Pt_CB_ACTIVATE,
done, closure);
PtRealizeWidget(dialog->widget);
PtUnblockWindows (blocked_list);
}
}
return (Pt_CONTINUE);
}
PhDim_t dim;
PtArg_t args[3];
int n;
PtWidget_t *window;
PtCallback_t callbacks[] = {{push_button_cb, NULL
}
};
char Helvetica14b[MAX_FONT_TAG];
if (PtInit(NULL) == -1)
exit(EXIT_FAILURE);
dim.w = 200;
dim.h = 100;
PtSetArg(&args[0], Pt_ARG_DIM, &dim, 0);
if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT,
1, args)) == NULL)
PtExit(EXIT_FAILURE);
callbacks[0].data = window;
n = 0;
PtSetArg(&args[n++], Pt_ARG_TEXT_STRING, "Count Down...", 0);
PtRealizeWidget(window);
PtMainLoop();
return (EXIT_SUCCESS);
}
If your modal dialog is self-contained and you just need to wait for it,
you might find this function useful:
ApModalWait()
Process Photon events until a given widget is destroyed
In this chapter. . .
Basic steps 711
Compiling and linking a non-PhAB application 712
Sample application 713
Connecting application code to widgets 716
Complete sample application 717
Basic steps
All applications using the Photon widget library follow the same basic
sequence:
1 Include <Pt.h>, the standard header file for the widget library.
CAUTION:
! The libphoton.so.1 library is for applications created with version
1.14 of the Photon microGUI only. Don’t combine this library with
the current libraries or header files, or your application won’t run
properly.
We recommend that you always link against the shared library. This
lets you keep your applications smaller and allows them to inherit
new features that are added to the widget library when new releases of
the shared library are installed.
The Photon library includes most of the function and widget
definitions. If your application uses Al (translation) or Px (extended)
functions, you’ll also need to link with the phexlib library. If your
application uses Ap (PhAB) functions, you’ll also need to link with
the Ap library.
The names of the shared and static libraries are the same. By default,
qcc links against the shared library; to link against the static library,
specify the -Bstatic option for qcc.
For example, if we have an application called hello.c, the command
to compile and link against the shared libraries is:
Sample application
The following example illustrates a very simple application using the
widget library. The program creates a window that contains a single
pushbutton.
/*
* File: hello.c
*/
#include <Pt.h>
if (PtInit(NULL) == -1)
PtExit(EXIT_FAILURE);
PtSetArg(&args[0], Pt_ARG_TEXT_STRING,
"Press to exit", 0);
PtCreateWidget(PtButton, window, 1, args);
PtRealizeWidget(window);
PtMainLoop();
return (EXIT_SUCCESS);
}
What’s going on
Although this is a simple application, a lot of work is being done by
each of these calls.
PtInit()
PtInit() calls PhAttach() to attach a channel to the Photon server, and
then initializes the widget libraries.
PtSetArg()
The PtSetArg() macro sets up an argument list that’s used to initialize
the button’s resources when it’s created. For more information, see
the Manipulating Resources in Application Code chapter.
PtRealizeWidget()
PtRealizeWidget() displays the widget and all its descendants in the
widget family. Our sample application calls PtRealizeWidget() for the
top-level window, so all the widgets in the application are displayed.
When a widget is realized, it uses the values of its resources to
determine how big it must be to display its contents. Before realizing
a widget, you should set any of the resources that may affect its size.
You may change some of the resources after the widget has been
realized, but it’s up to the widget to determine if it can or will resize to
accommodate the change in the resource’s value.
You can set resize flags that the widget uses to determine whether or
not to adjust its size in response to such changes, but note that if the
widget exceeds the dimensions allocated to it by its parent, it’s
clipped to the parent’s size. There’s no mechanism for the widget to
negotiate with its parent to obtain more space. See the Geometry
Management chapter for more information.
If a Photon region is required to display the widget correctly, it’s
created each time the widget is realized. A region is required under
any of the following conditions:
PtMainLoop()
Calling PtMainLoop() transfers control of the application to the
Photon widget library.
The widget library waits for Photon events and passes them on to the
widgets to handle them. Application code is executed only when
callback functions that the application has registered with a widget are
invoked as a result of some event.
Callbacks
A callback resource is used to notify the application that a specific
action has occurred for a widget (e.g. you’ve selected a button). Every
callback resource represents some user action that we thought your
application might be interested in.
As with all resources, a widget has its callback resources defined by
its widget class, and it inherits the callback resources defined by all
the ancestors of its class. This means that a widget may have several
user actions that it can notify the application about.
The value of a callback resource is a callback list. Each element of the
list is an application function to be called in response to the behavior
and client data associated with the callback. Client data is a pointer to
any arbitrary data that your application may need to provide to the
callback function for it to work correctly.
For information about callbacks, see “Callbacks” in the Managing
Widgets in Application Code chapter.
Event handling
When we create a widget class, we can’t possibly anticipate all of
your application’s needs. Your application may want to be notified of
some occurrence on a widget that doesn’t have an associated callback
resource. In such cases, your application can define event-handling
functions.
For information about event handlers, see “Event handlers” in the
Managing Widgets in Application Code chapter.
if (PtInit(NULL) == -1)
PtExit(EXIT_FAILURE);
n = 0;
PtSetArg(&args[n++], Pt_ARG_TEXT_STRING,
"Press to exit", 0);
if(PfGenerateFontName("Helvetica", 0, 14,
Helvetica14) == NULL) {
perror("Unable to generate font name");
} else {
PtSetArg(&args[n++], Pt_ARG_TEXT_FONT, Helvetica14, 0);
}
PtSetArg(&args[n++], Pt_CB_ACTIVATE, callbacks,
sizeof(callbacks)/sizeof(callbacks[0]));
PtCreateWidget(PtButton, window, n, args);
PtRealizeWidget(window);
PtMainLoop();
return (EXIT_SUCCESS);
}
int
push_button_cb(PtWidget_t *w, void *data,
PtCallbackInfo_t *cbinfo)
{
printf( "I was pushed\n " );
PtExit( EXIT_SUCCESS );
return( Pt_CONTINUE );
}
In this appendix. . .
Event space 721
Events 722
Regions 723
Event types 728
How region owners are notified of events 728
Device region 729
Photon drivers 731
Photon window manager 734
Event space
The essential characteristic of Photon is the way in which graphical
applications are represented. All Photon applications consist of one or
more rectangles called regions. These regions reside in an abstract,
three-dimensional event space; the user is outside this space looking
in.
Event space
Root region
Application region
Regions can emit and collect objects called events. These events can
travel in either direction through the event space (i.e. either toward or
away from the user). As events move through the event space, they
interact with other regions — this is how applications interact with
each other. The process maintaining this simple architecture is the
Photon Manager.
Events
As an event flows through the event space, its rectangle set intersects
with regions placed in the event space by other applications. As this
occurs, the Photon Manager adjusts the event’s rectangle set according
to the attributes of the regions with which the event intersected.
Region
C Tile Tile Tile
1 2 3
Region
D Tile
4
Region
A
Region
Event received by
B
Region D
Certain event types (e.g. button presses) have no need for their initial
rectangle set to have the dimensions of the emitting region. For such
events, the rectangle set consists of a single rectangle whose size is a
single point. A single-point rectangle set is called a point source.
For a list of the event types, see the “Event types” section.
Regions
A process may create or use any number of regions and may place
them anywhere in the event space. Furthermore, by controlling the
dimensions, location, and attributes of a region (relative to the other
regions in the event space), a process can use, modify, add, or remove
services provided by other regions.
Photon uses a series of regions, ranging from the Root region at the
back of the Photon event space to the Graphics region at the front.
Root
Workspace (PWM)
Application
Focus (PWM)
Device
Input Group
Pointer/Keyboard
Graphics
• sensitivity
• opacity
You can set these independently for each different type of event.
Sensitivity
If a region is sensitive to a particular type of event, then the region’s
owner collects a copy of any event of that type that intersects with the
region. If other regions are sensitive to this same event type and the
event intersects with them, they’ll also collect a copy of the event —
but with a potentially different rectangle set.
Although many regions can collect a copy of the same event, the
rectangle set for the event may be adjusted and will therefore be
unique for each region that collects the event. The rectangle set
reflects the event’s interaction with other regions in the event space
before arriving at the collecting region.
If a region isn’t sensitive to an event type, the region’s owner never
collects that type of event.
The sensitivity attribute doesn’t modify the rectangle set of an event,
nor does it affect the event’s ability to continue flowing through the
event space.
Opacity
Opaque regions block portions of an event’s rectangle set from
traveling further in the event space. The opacity attribute controls
whether or not an event’s rectangle set is adjusted as a result of
intersecting with a region.
If a region is opaque to an event type, any event of that type that
intersects with the region has its rectangle set adjusted in order to
“clip out” the intersecting area. This changes the rectangle set such
that it includes smaller rectangles. These new rectangles describe the
portions of the event that remain visible to regions beyond this region
in the event space.
If a region isn’t opaque to an event type, then events of that type never
have their rectangle set adjusted as a result of intersecting with that
region.
Attribute summary
The following table summarizes how a region’s attributes affect
events that intersect with that region:
If the region is: Then the event is: And the rectangle set is:
Not sensitive, Ignored Unaffected
not opaque
Not sensitive, Ignored Adjusted
opaque
Sensitive, not Collected Unaffected
opaque
Sensitive, Collected Adjusted
opaque
Event logging
By placing a region across the entire event space, a process can
intercept and modify any event passing through that region. If a
region is sensitive to all events, but not opaque, it can transparently
log all events.
Event modification
If a region is sensitive and opaque, it can choose to reemit a modified
version of the event. For example, a region could collect pointer
events, perform handwriting recognition on those events, and then
generate the equivalent keyboard events.
Parent/child relationships
All regions have parent/child relationships. A child region is always
placed in front of the parent region (i.e. closer to the user), and its
coordinates are relative to the parent’s region.
Upper-left Upper-right
quadrant quadrant
(0, 0)
VGA
display
(640, 480)
Lower-left Lower-right
quadrant quadrant
Root region
A special region called the root region is always the region furthest
away from the user. All other regions descend in some way from the
root region. Once an event traveling away from the user reaches the
root region, it ceases to exist.
The root region’s dimensions are the width of the entire Photon
coordinate space. As a result of the parent/child relationship of all
regions, any region’s location is ultimately related to the root region’s
dimensions.
A region can be located anywhere in the event space and still have the
root region as its parent.
Event types
Events are emitted for the following reasons:
• boundary crossings
• drag operations
• drag-and-drop operations
• drawing functions
• polling
• synchronous notification
• asynchronous notification
Polling
To poll, the application calls a function that asks the Photon Manager
to reply immediately with either an event or a status indicating no
event is available.
Normally you should avoid polling, but you may find it beneficial on
occasion. For example, an application rapidly animating a screen can
poll for events as part of its stream of draw events. An application can
also use polling to retrieve an event after asynchronous notification.
Synchronous notification
For synchronous notification, the application calls a function that asks
the Photon Manager to reply immediately if an event is pending, or to
wait until one becomes available before replying.
With synchronous notification, an application can’t block on other
sources while it’s waiting for the Photon Manager to reply. You
should find this behavior acceptable in most cases since it causes the
application to execute only when the desired events become available.
But if for some reason the possibility of blocking on the Photon
Manager isn’t acceptable, you may consider asynchronous
notification.
Asynchronous notification
For asynchronous notification, the application calls a function that
sets up a notification method (e.g. a signal or a pulse) that the Photon
Manager activates when an event of the desired type is available. The
application can then retrieve the event by polling.
With asynchronous notification, an application can block on multiple
sources, including processes that aren’t Photon applications.
Device region
The device region is owned by the Photon Manager, which divides the
event space into two sections:
• driver regions, which reside on the user’s side of the device region
The Photon Manager uses the device region to focus pointer and
keyboard events as well as to manage drag events.
Pointer focus
As with other windowing systems, Photon has the concept of a
pointer (i.e. screen cursor). This pointer is graphically represented on
the screen and tracks the movements of the pointing device (e.g. a
mouse or touchscreen). Drivers for pointing devices emit pointer
events toward the root region.
A pointer event emitted from a driver is unfocused, or raw, until it
arrives at the device region, where the Photon Manager intercepts it
and then assigns it a location in the Photon coordinate space.
Assigning this location — which is known as focusing the event —
controls which regions will collect the event. The Photon Manager
then reemits the event with the focused location.
Because Photon emits focused, or cooked, pointer motion events in
both directions from the device region, application programs as well
as driver programs can be informed of pointer actions. For example,
when the graphics driver collects focused pointer events, it updates
the location of the pointer’s graphical image on the screen.
Keyboard focus
The keyboard driver is similar to pointing device drivers, except it
emits keyboard events. As with pointer events, keyboard events are
unfocused until they arrive at the device region, where the Photon
Manager assigns them a location (i.e. focuses them) in the Photon
coordinate space.
By default, the device region sets the same focus location for both
keyboard events and pointer events. Therefore, regions directly
behind the screen pointer will collect focused keyboard events.
Drag events
An application initiates dragging by emitting a drag event to the
device region. Once this event is collected at the device region, the
Photon Manager takes care of the interaction with the pointer (i.e.
drag rectangle) until the drag operation is complete. Once completed,
the device region emits a drag event to the application.
Drag-and-drop events
During a drag-and-drop operation, a series of events is emitted to
advise the widgets involved of the operation’s status. Some of these
events are emitted to the source of the operation, and others to the
destination. For more information, see the Drag and Drop chapter.
Photon drivers
In Photon, drivers aren’t inherently different from other applications.
They’re simply programs that use regions and events in a particular
way to provide their services. Depending on its function, a driver is
either an input driver or an output driver.
For example, the mouse and keyboard drivers are input drivers
because they emit, and are the source of, hardware actions. Graphics
drivers, on the other hand, are output drivers because they collect
events that cause them to take action with hardware devices.
Input drivers
Mouse driver
The mouse driver places a region on the user’s side of the device
region. It gets information from the mouse hardware and builds
Photon raw pointer events that it then emits toward the root region.
When the device region collects a raw pointer event, the Photon
Manager focuses it and then emits cooked events in both directions in
the event space.
Keyboard driver
The keyboard driver also places a region on the user’s side of the
device region. The driver gets information from the keyboard
hardware and builds Photon keyboard events that it then emits toward
the root region.
When the device region collects keyboard events, the Photon Manager
focuses those events and then reemits them toward the root region.
Output drivers
Graphics driver
A graphics driver places a region sensitive to draw events onto the
user’s side of the device region. As the driver collects draw events, it
renders the graphical information on the screen. Because the collected
event’s rectangle set contains only those areas that need to be updated,
the driver can optimize its update. (This is especially efficient if the
graphics hardware can handle clipping lists directly.)
The Photon drawing API accumulates draw requests into batches that
are emitted as single draw events.
Encapsulation drivers
Since graphics drivers for Photon are really just applications, they can
display the graphical output of Photon inside another windowing
system (for example, the X Window System). A Photon driver could
also take the keyboard and mouse events it collects from the X system
and regenerate them within Photon, allowing the Photon window in
the X system to be fully functional, both for graphical display and for
keyboard/mouse input.
• Window-frame regions
• Focus region
• Workspace region
• Backdrop region
Window-frame regions
Most applications rely on the windowing system to provide the user
with the means to manipulate their size, position, and state (i.e.
open/iconified). In order for the user to perform these actions, the
window manager puts a frame around the application’s region and
then places controls in that frame (e.g. resize corners, title bars,
buttons). We refer to these controls as window services.
To indicate it can provide window services, the window manager
registers with the Photon Manager. When an application opens a
Focus region
As mentioned earlier, the device region focuses keyboard events to
regions directly behind the screen pointer. But by placing a region of
its own (i.e. the focus region) just behind the device region, the
window manager intercepts these keyboard events as they’re emitted
from the device region and implements an alternate focus method.
The window manager can redirect keyboard events to regions not
directly beneath the screen pointer. For example, it can focus events
toward the last window the user “clicked” on (i.e. the active window).
The window manager can direct keyboard events to that active region
even if the region gets covered by another region.
Workspace region
From the user’s perspective, the workspace is the empty space
surrounding the windows on the screen. The window manager places
a workspace region just in front of the root region to capture pointer
events before they get to the root region and thus disappear. When the
user presses a pointer button and no region collects the event, the
window manager brings up a workspace menu that lets the user select
a program to run.
Backdrop region
Users often like to have an ornamental backdrop image displayed
behind the windows on the screen. To display such a bitmap, the
window manager places a backdrop region in the event space.
PtCalendar Calendar
N/A PtClient Superclass for client
widgets — not normally
instantiated
continued. . .
PtEllipse Ellipse
continued. . .
continued. . .
PtOSContainer Offscreen-context
container, useful for
drawing flicker-free
images and animations
continued. . .
PtRect Rectangle
N/A PtRegion Photon region—must be
created with
PtCreateWidget()
N/A PtScrollArea Superclass for scrolling
widgets—not normally
instantiated
PtScrollBar Scrollbar
continued. . .
PtSeparator Separator
N/A PtServer Server widget — must
be created with
PtCreateWidget()
PtTimer Timer
PtUpDown Increment/decrement
button
continued. . .
In this appendix. . .
Wide and multibyte characters 749
Unicode 750
UTF-8 encoding 750
Conversion functions 752
Other encodings 754
Keyboard drivers 755
Photon compose sequences 756
wide character
A character represented as a value of type wchar_t, which
typically is larger than a char.
multibyte character
A sequence of one or more bytes that represents a character,
stored in a char array. The number of bytes depends on the
character.
wide-character string
An array of wchar_t.
multibyte string
A sequence of multibyte characters stored in a char array.
Unicode
Unicode is a 32-bit encoding scheme:
• It packs most international characters into wide-character
representations (two bytes per character).
• Codes between 128 and 255 define the same characters as in the
ISO 8859-1 character set.
Glyphs Range
Nondisplayable keys 0xF000 – 0xF0FF
Cursor font 0xE900 – 0xE9FF
UTF-8 encoding
Formerly known as UTF-2, the UTF-8 (for “8-bit form”)
transformation format is designed to address the use of Unicode
character data in 8-bit UNIX environments. Each Unicode value is
encoded as a multibyte UTF-8 sequence.
Here are some of the main features of UTF-8:
• For all subsequent bytes in a multibyte encoding, the first two bits
are 10. The value of a trailing byte in a multibyte encoding is
always greater than or equal to 0x80.
The following table shows the binary form of each byte of the
encoding and the minimum and maximum values for the
characters represented by 1-, 2-, 3-, and 4-byte encodings:
• Where there’s more than one way to encode a value (such as 0),
the shortest is the only legal value. The null character is always a
single byte.
Conversion functions
In our C libraries, “wide characters” are assumed to be Unicode, and
“multibyte” is UTF-8 in the default locale. The wchar_t type is
defined as an unsigned 32-bit type, and wctomb() and mbtowc()
implement the UTF-8 encoding in the default locale.
You can use the following functions (described in the QNX Neutrino
Library Reference) for converting between wide-character and
multibyte encodings:
Other encodings
If your application needs to work with other character encodings,
you’ll need to convert to and from UTF-8. Character sets are defined
in the file /usr/photon/translations/charsets, and include:
• Big5 (Chinese)
• Cyrillic (KOI8-R)
• Japanese (EUC)
• Japanese (Shift-JIS)
• Korean (EUC)
PxTranslateFromUTF()
Translate characters from UTF-8
PxTranslateList()
Create a list of all supported character translations
PxTranslateSet()
Install a new character-set translation
PxTranslateStateFromUTF()
Translate characters from UTF-8, using an internal state buffer
PxTranslateStateToUTF()
Translate characters to UTF-8, using an internal state buffer
PxTranslateToUTF()
Translate characters to UTF-8
PxTranslateUnknown()
Control how unknown encodings are handled
These functions are supplied only in static form in the Photon library
phexlib. The prototypes are in <photon/PxProto.h>.
Keyboard drivers
The keyboard driver is table-driven; it handles any keyboard with 127
or fewer physical keys.
A keypress is stored in a structure of type PhKeyEvent_t (described
in the Photon Library Reference).
These aren’t keychords; press and release each key one after the other.
continued. . .
If your keyboard doesn’t have the following symbols, you can create
them by pressing the Alt key, followed by the first key in the sequence,
followed by the second key in the sequence.
continued. . .
continued. . .
continued. . .
continued. . .
continued. . .
In this appendix. . .
Assumptions 765
Introduction 765
The basics 768
Caveats 775
Example 777
Example: Using the IDE’s System Builder 785
Advanced Topics 787
Assumptions
This appendix makes the following assumptions:
• You have your target hardware booting into Neutrino, and can run
a shell and commands such as pidin.
• You know what graphics hardware you will be using, and its
parameters (such as the Vendor and Device IDs for a PCI graphics
card).
Introduction
The Photon microGUI is an embedded Graphical User Interface
(GUI). This GUI is made up of numerous processes that use Neutrino
message passing in order to create a highly responsive user
experience. Photon is made up of these main components:
• user applications.
Photon Server
The Photon server is the core server process for the GUI. This
process must be the first graphical process run in the system. It is
responsible for handling region creation and destruction, clipping, and
managing the Photon event space.
Graphics subsystem
This process, io-graphics, handles the Photon draw stream and
loads the hardware driver. This process runs before any user
application processes. You must specify the following when running
this process:
• the graphics driver (a devg-* driver)
• the graphics resolution and color depth (for example: 640×480, 24
bit)
• specific hardware parameters for a LCD panel or PCI video card
Font support
This process (phfont) and associated libraries are used to render and
gather metrics about fonts. Photon can render the following types of
fonts:
• Adobe Type 1 (.pfa)
• Adobe Type 2 (.cff)
• Bitstream Speedo — public encryption only (.spd)
• Bitstream Stroke (.ffs)
• Bitstream T2K (.t2k)
• Bitstream TrueDoc (.pfr)
• Photon bitmap (.phf)
• TrueType (.ttf)
• TrueType collections (.ttc)
Input support
This process is responsible for handling user input from a mouse,
keyboard, or touchscreen. This process communicates with your input
hardware and then emits Photon events, which are collected and
delivered to graphical processes in the system.
User applications
Once all of the other processes are running you can start user
applications.
The basics
Step 1. Export environment variables
The PHOTON_PATH environment variable points to the base
directory of the Photon installation. By default, this directory is
/usr/photon. This location is expected to hold at least the
following subdirectories:
font_repository
Photon font files and configuration files used by the
font server (platform-independent).
translations
Photon language translations (platform-independent)
These files are required only if your application(s)
handles non-UTF8 character encodings via the
PxTranslate*() API.
PHOTON_PATH=/usr/photon
Photon
Files needed
/usr/photon/bin/Photon
/lib/dll/phfont.so
Font manager plugin.
/lib/libfont.so
Font manager API library.
/lib/dll/font/ttfFFcore.so, /lib/dll/font/FCcore.so,
and /lib/dll/font/PHFcore.so
Rendering plugins. Use the use utility to view specific support
information for these plugins.
/lib/libblkcache.so
Disk block cache library, used by ttfFFcore.so,
FCcore.so, and PHFcore.so.
/lib/libFF-T2K.so
Bitstream FontFusion rendering library, used by
ttfFFcore.so and FCcore.so.
/lib/libFF-T2K-fm.so
Bitstream FontFusion font management library for font
collections (.pfr and ttc), used by FCcore.so.
/lib/libFF-T2K-cache.so
Bitstream FontFusion font cache management library, used by
FCcore.so and ttfFFcore.so.
/usr/photon/bin/io-graphics
Graphics subsystem executable.
/lib/dll/gri-photon.so
Photon draw event handler.
/usr/lib/libgri.so
Utilities library for Photon event handling
/usr/lib/libphrender.so
Photon rendering routines (required by gri-photon.so).
/lib/libfont.so
Font manipulation library (also required by Photon
applications).
/lib/dll/phfont.so
Font server plugin.
/usr/photon/palette/file
A Photon palette file for the target display.
/usr/lib/libffb.so.2
Software fallback routines for graphics drivers.
/usr/lib/libdisputil.so.2
Miscellaneous utilities for graphics drivers.
Make sure that all required libraries are accessible by the dynamic
loader before you start io-graphics. Use the
LD_LIBRARY_PATH environment variable or _CS_LIBPATH
configuration string to point to the location of the shared libraries.
Files needed
The appropriate devi-* driver in /usr/photon/bin
The appropriate .kbd keyboard mapping file in
/usr/photon/keyboard
Files needed
/usr/photon/bin/pwm
Files needed
• Your application files
Caveats
The following are observations that some customers have encountered
when moving Photon to an embedded system.
mkifs
By default, mkifs strips PhAB resource names from executable files.
To prevent this, specify the +raw attribute for all Photon Application
Builder applications. For example:
[+raw]/usr/photon/bin/phcalc
Flash filesystems
The following flash filesystem properties affect how you configure
Photon:
$(ABOBJ) $(MYOBJ)
$(LD) $(LDFLAGS) $(ABOBJ) $(MYOBJ) -M -o mine
usemsg mine ../Usemsg
phabbind mine $(ABMOD)
to:
$(ABOBJ) $(MYOBJ)
$(LD) $(LDFLAGS) $(ABOBJ) $(MYOBJ) -M -o mine
usemsg mine ../Usemsg
phabbind mine.res $(ABMOD)
Graphics
Many embedded systems lack components that are typical on an x86
desktop machine, such as BIOS ROMs. Because many of the
modeswitchers that Photon supports require a video BIOS to allow
them to switch graphics modes, you might need a BIOS on the board.
Check with us to see if a non-BIOS version is available.
Miscellaneous
Here are some other considerations:
Scrolling If the scrolling area pages down more than one page
at a time when you click in the trough, try increasing
the value of the mouse repeat delay in Photon. For
example:
Example
Let’s look at the steps involved in embedding Photon for use in an
embedded system by creating a simple buildfile that contains a few
simple Photon applications.
Our goal is to build a Photon system with the following minimal
capabilities that satisfies our system’s requirements:
• Troubleshooting
Required binaries
The first step involves figuring out all the binaries required to run
Photon. You can see everything that’s running on a full system. Run
Photon on your PC, and look at the output of the pidin arg
command.
From that list, you need only a few of the programs:
Save the argument list for your system in a file. We’ll need that output
later.
Required libraries
On this embedded system you want only the components listed above,
plus you’ll run a couple of simple applications:
Run the applications, then look at the output of the pidin mem
command. The resulting listing tells you every library that you need
to make available to the embedded system. For a graphics driver,
you’ll use the generic SVGA driver (devg-svga.so).
So you need the following libraries (at least):
• libph.so.3
• libphexlib.so.3
• libphrender.so.2
• libffb.so.2
• libdisputil.so.2
• libgri.so.2
• libAp.so.3
• libm.so.2
• devg-svga.so
Required fonts
Now let’s look at fonts. Sometimes an application expects a specific
font, and codes directly to that font. If this is the case, you need to
explicitly include every font that your application needs. If you
standardize on a certain family/style of fonts or if you don’t care what
exact font you have (as long as the size is okay), then you can cut
down on the number of fonts and use one font to replace several other
families of fonts. For example, you can use Times as a replacement
for Helvetica and Courier.
In this example, because you’re using a few simple applications, and
because you’re trying to create the smallest image possible, you need
only two fonts: a monospace and regular TrueType version of Prima
Sans.
Now’s a good time to create a play area on your system to begin
testing the embedded system, and collecting required files.
Create a subdirectory called phembed in your home directory (or
whichever directory you wish to keep your source files). Within that
directory, create these subdirectories:
• phembed/bin
• phembed/lib
• phembed/font_repository
Now back to the fonts. In this example, you want to use the
primasansmonobts TrueType font for everything. You’ll also want
to use a mouse, so you’ll include the phcursor.phf file.
Here are the files you need:
• fontmap
• fontopts
• phcursor.phf
• tt2009m_.ttf
You need to modify the fontmap and fontopts files to reflect the
fonts, options and mappings you want for your embedded system.
You can edit these files by hand (see phfont for more information on
the structure of these files). In our case lets make sure that the
fontmap file contains:
? = primasansmonobts
This ensures that all unknown fonts will be replaced with the
primasansmonobts font, provided in the tt2009m_.ttf file.
To generate fontdir, use the mkfontdir like this:
mkfontdir -d /phembed/font_repository
• In a real buildfile, you can’t use a backslash (\) to break a long line
into shorter pieces, but we’ve done that here, just to make the
buildfile easier to read.
slogger &
SYSNAME=nto
TERM=qansi
PHOTON=/dev/photon
PATH=:/proc/boot:/usr/bin:/bin:/usr/photon/bin
LD_LIBRARY_PATH=:/proc/boot:/usr/lib:/lib:/lib/dll
PHOTON_PATH=/usr/photon
PHOTON=/dev/photon
PHFONT=/dev/phfont
HOME=/
# standard libs
libc.so
libm.so
# photon libs
libph.so
libAp.so
libphexlib.so
# io-graphics libs
gri-photon.so
libphrender.so
libgri.so
libdisputil.so
libffb.so
# graphics driver
devg-svga.so
/etc/system/config/crtc-settings = /etc/system/config/crtc-settings
/usr/photon/palette/default.pal = /usr/photon/palette/default.pal
# font libs
/lib/dll/font/ttfFFcore.so = /lib/dll/font/ttfFFcore.so
/lib/dll/font/PHFcore.so = /lib/dll/font/PHFcore.so
libfontharnessutils.so
libblkcache.so
libFF-T2K.so
libFF-T2K-cache.so
libFF-T2K-fm.so
libfont.so
phfont.so
# font config
/usr/photon/font_repository/tt2009m_.ttf = /usr/photon/font_repository/tt2009m_.ttf
/usr/photon/font_repository/phcursor.phf = /usr/photon/font_repository/phcursor.phf
/usr/photon/font_repository/fontopts = /usr/photon/font_repository/fontopts
/usr/photon/font_repository/fontdir = /phembed/font_repository/fontdir
/usr/photon/font_repository/fontmap = /phembed/font_repository/fontmap
# input config
/usr/photon/keyboard/en_US_101.kbd = /usr/photon/keyboard/en_US_101.kbd
[data=c]
devc-pty
ksh
slogger
sloginfo
Photon
io-graphics
devi-hirun
pwm
[+raw] /usr/photon/bin/pterm = pterm
[+raw] /usr/photon/bin/phcalc_sm = phcalc_sm
# allow pterm to save its configuration to RAM, if the user changes it.
[type=link] /.ph/pterm = /dev/shmem
• After the system starts io-graphics, you check that the font
server is running correctly (waitfor /dev/phfont).
• You use the [+raw] directive for the PhAB applications so that
mkifs doesn’t remove necessary resource information from these
files.
Once you’ve built your image using mkifs, you can transfer it to a
test machine to see how it works. See "Transferring an OS image
onto your board" in the Working with a BSP chapter of Building
Embedded Systems for more information.
Troubleshooting
1 When I start io-graphics, it seems to be running, but nothing
appears on the screen.
Check the system log; io-graphics may have sent error
messages to the system logger, slogger. In order to debug the
problem, make sure slogger is running before starting the
graphics driver. Use sloginfo to display the system log
messages.
for more information. Since setting the attribute will cause the
application not to be stripped, you may want to use the strip
utility to manually strip the binary before building the image, to
reduce the image size.
Here are the general steps required to make a System Builder project
that’s identical to the previous buildfile example:
• io-graphics
• devi-hirun
• pwm
• /usr/photon/bin/pterm
• /usr/photon/bin/phcalc_sm
You need to use the equivalent of [+raw] on pterm and
phcalc_sm by selecting them, and setting the Strip File
property to No.
3 Next, add the required libraries. You’ll find that some of them
are already in the project, as the System Builder identifies
libraries required by binaries, and adds them automatically.
Standard libraries:
• libm.so
Photon libraries:
• libph.so
• libAp.so
• libphexlib.so
Graphics libraries:
• libphrender.so
• libgri.so
• libdisputil.so
• libffb.so
Font libraries:
• libfontutils.so
• libblkcache.so
• libFF-T2K.so
• libFF-T2K-cache.so
• libFF-T2K-fm.so
• libfont.so
• phfont.so
4 Now add the DLLs:
• devg-svga.so
• /lib/dll/font/ttfFFcore.so
• /lib/dll/font/PHFcore.so
5 Add these required files.
Fonts and the font configuration files:
• /usr/photon/font_repository/tt2009m_.ttf
• /usr/photon/font_repository/phcursor.phf
• /usr/photon/font_repository/mappings
• /usr/photon/font_repository/fontopts
• /usr/photon/font_repository/fontkey
Other required configuration files:
• /etc/system/config/crtc-settings
• /usr/photon/palette/default.pal
• /usr/photon/keyboard/en_US_101.kbd
6 Finally set up these symbolic links:
• /bin/sh = /proc/boot/ksh
• /dev/console = /dev/ser1
• /tmp = /dev/shmem
You can now build the image, transfer it to your target, and run it.
Advanced Topics
This section covers some of the more advanced topics in embedding
Photon. It covers:
• Configuring fonts
• Adding a splash screen
Configuring fonts
Configuring fonts and installing the font server components in the
correct location is the most difficult part of embedding Photon.
To configure the font system, you need to:
Internal or external?
The first decision you must make about the font service is how the
server is started. It can run as a stand alone process (we refer to this as
an external server) or as a plugin to io-graphics (which we call an
internal server).
We recommend to run an external server in these conditions:
Required fonts
You can map, or substitute, font names by using the fontmap file.
For more information on the format of fontmap and other font
configuration files, see phfont in the QNX Neutrino Utilities
Reference.
For more information about the format of each of these files, see
phfont.
You can configure the fonts on the embedded system itself, but it’s
easier to use your development system to configure the fonts to mimic
the desired configuration for the embedded system, then assemble the
font data and configuration files in the appropriate Embedded File
System (EFS) build image directory.
fontdir This file needs to list only the fonts you’re installing in
/usr/photon/font_repository. You can edit this
file in one of two ways:
• Edit the default existing fontdir file by hand,
removing all the lines that refer to fonts you’re not
including in your image.
Or:
• Generate this file using the mkfontdir utility (on
all hosts).
?=primasansbts
In this appendix. . .
Photon in a single window 797
Exiting PhAB 797
Advanced options 798
PHINDOWSOPTS 798
Transferring PhAB projects 799
Debugger launch line 799
Custom widget development and PhAB 800
Using custom TrueType fonts and PhAB 802
Photon Hook DLLs 802
Exiting PhAB
When you exit PhAB, it attempts to shut down all Photon components
as well, unless there are other applications still running within the
Photon window (such as a second PhAB session or the language
editor).
If all Photon components don’t shut down automatically, or if you just
want to force everything to exit in the event that the system is having
problems, you should manually close the Console for PhAB window.
If this does not work, type:
ph -kill
Advanced options
If you wish to specify command-line options to pwm or to the photon
server, you can use these environment variables:
• PWMOPTS
• PHOTONOPTS
You set these environment variables using the Environment tab (in the
System program of the Windows Control Panel).
For details on the command-line options for any QNX Neutrino
utility, see the Utilities Reference.
PHINDOWSOPTS
You can use the PHINDOWSOPTS environment variable to pass
extra options to the special version of Phindows that’s used as the
display driver for PhAB for Windows. For example, if your Neutrino
target is using an 8-bit 256 color mode with a nondefault palette, then
to allow the PhAB display on the Windows host to show the colors as
they’ll appear on the target, you can do the following:
1 Set the Windows display mode to 256 colors.
In Windows XP, unlike Windows NT/2000, you do this on a
per-application basis, by using the Compatibility tab on the
properties for the application’s shortcut or executable.
2 Set the PHINDOWSOPTS environment variable to specify the
same palette file as will be used on the target. For example, if
the palette file is called grey.pal, you might set this variable
to -P%QNX_TARGET%/usr/photon/palette/grey.pal. In
this case, the direction of the slashes doesn’t matter.
To set environment variables globally, you can use the Environment
Variables button in the Advanced tab of the System program in the
Windows Control Panel. To set a variable temporarily, use set
variable= in a Command Prompt window, and then type phab to run
the Photon Application Builder with that environment variable set.
the remote target. Here are the initial commands from a typical debug
session after you start GDB using the default launch line:
For the above, we assume that we’re connected to the target machine
via serial port com1 and that the pdebug remote debug agent is
already running on the target.
If you want to use a graphical debugger, use the IDE that’s part of the
QNX Momentics Professional Edition. Create a Photon Appbuilder
project within the IDE and launch PhAB from there.
won’t take effect and be displayed until you run your application on
the target.
For the following procedure, we assume that you’ve already
performed the steps that aren’t specific to the host platform, namely:
After you’ve done all the above, you can begin to use the custom
widget class in PhAB. To make PhAB display custom widgets
correctly as you use them, follow these additional Windows-specific
steps:
The next time you start PhAB after completing these steps, you should
be able to see custom widgets displayed correctly as you work with
them. If not, consult the PhAB console window for error messages.
1 Install the font in Windows using the control panel. See the
Windows help for more information on this step.
The font is now available when you run PhAB standalone or in the
IDE.
set PHOTON_HOOK=c:/my_photon_hooks
appbuilder
Or you can type the following from bash:
export PHOTON_HOOK=c:/my_photon_hooks
appbuilder
• Project management
• Editing
• Adding items
• Building
• Widget management
• Other shortcuts
Editing shortcuts
Command Shortcut
Undo last action Ctrl-Z
continued. . .
Command Shortcut
Build and run F6*
Build and debug F5*
Rebuild all F3*
Build F7*
Make clean Shift-F3*
Generate UI Shift-F7
Run arguments Shift-F6*
Manage targets F11*
continued. . .
Other shortcuts
continued. . .
Glossary 813
accelerator
See hotkey.
activate
A widget is usually activated when you release a mouse button while
pointing at an armed widget.
active window
The window that currently has focus.
anchor offset
The distance between the edges of a widget and the parent widget it’s
anchored to.
anchor
A constraint mechanism used to manage what happens to a widget
when its parent is expanded or contracted. For example, a pane that’s
anchored to the sides of a window expands or contracts as the
window’s size is changed.
application region
A region that belongs to a Photon application (as opposed to a Photon
system process, such as the window manager, graphics drivers, etc.).
An application region is usually placed behind the device region.
Also called a window region.
argument list
An array of type PtArg_t used when setting and getting widget
resources.
arm
A widget is usually armed when you press a mouse button while
pointing at it.
Glossary 815
backdrop
An image that’s displayed as a background on your screen.
backdrop region
A region placed behind all windows to display a background image.
balloon
A small box that pops up to define or explain part of the user interface.
A balloon is displayed when the pointer pauses over a widget.
bitmap
A color picture consisting of one or more bitplanes.
bitplane
An array of bits representing pixels of a single color in a bitmap.
blit
An operation that moves an area of a graphics context (e.g. the
screen) to another area on the same or a different context.
callback
A callback function or a callback resource.
callback function
Code connecting an application’s user interface to its code. For
example, a callback is invoked when you press a button.
callback resource
A resource that specifies a list of functions and their client data to be
called when a certain action occurs.
canvas
The part of a widget that’s used for drawing. For PtWidget, this is
the area inside the widget’s borders. For PtBasic and its
descendants, the canvas is the area inside the widget’s border and
816 Glossary
margins. Other widgets, such as PtLabel, may define additional
margins.
class
See widget class.
class hierarchy
The relationships between all of the widget classes.
client data
Any arbitrary data the application may need to provide to a callback
function.
clipping list
An array of rectangles used to restrict output to a particular area.
clipping rectangle
A rectangle used to restrict output to a particular area.
CMY value
A color expressed as levels of cyan, magenta, and yellow.
CMYK value
A color expressed as levels of cyan, magenta, yellow, and black.
color depth
The number of bits per pixel for a screen or pixmap.
Glossary 817
Common User Access
See CUA.
compose sequence
A sequence of key presses that can be used to type a character that
might not appear on the keyboard.
console
One of nine virtual screens on the desktop. Also called a workspace.
consume
When a widget has processed an event and prevents another widget
from interacting with the event, the first widget is said to have
consumed the event.
container
A widget that can have other widgets as children. For example,
PtWindow, PtGroup, and PtOSContainer.
cooked event
A key or pointer event that has been assigned a location in the Photon
event space. Also called a focused event.
CUA
Common User Access — a standard that defines how you can change
focus by using the keyboard.
current item
The item in a list or tree widget that will be selected (or perhaps
unselected) when you press Enter or Space. It’s typically drawn with
a blue dotted line around it when its widget has focus.
818 Glossary
cursor
An indicator of a position on a screen, such as a pointer or an
insertion point in a text field.
damaged
Whenever a widget needs to be redisplayed due to a change in the
window (e.g. the widget is changed, moved, or realized), it’s said to
be damaged.
dead key
A key that, when pressed, doesn’t produce a symbol, but initiates a
compose sequence.
default placement
The placement of a region when no siblings are specified. The
opposite of specific placement.
desktop
The virtual screen, consisting of nine consoles or workspaces.
device region
The region located in the middle of the event space, with application
regions behind it and driver regions in front of it (from the user’s
point of view).
dialog module
A PhAB module similar to a window module, except that a dialog
module can have only one instance per process.
direct-color
A color scheme in which each pixel is represented by an RGB value.
Contrast palette-based.
Glossary 819
disjoint parent
A disjoint widget that’s the ancestor of another widget.
disjoint widget
A widget that can exist without a parent. If a disjoint widget has a
parent, it can exist outside its parent’s canvas. For example,
PtWindow, PtMenu, and PtRegion are disjoint widgets, but
PtButton, PtBkgd, and PtRect aren’t.
A disjoint widget owns regions that aren’t children of its parent’s
regions. Any clipping set by the parent of a disjoint widget isn’t
applied to the disjoint widget. The regions of disjoint widgets are
sensitive and opaque to expose events.
dithering
A process whereby pixels of two colors are combined to create a
texture or a blended color.
draw context
A structure that defines the flow of the draw stream. The default draw
context emits draw events to graphics drivers. Print contexts and
memory contexts are types of draw contexts.
draw stream
A series of tokens that are dispatched via draw events and can be
collected by a rendering engine such as a graphics driver.
driver region
A region created by a driver, usually placed in front of the device
region.
encapsulation driver
A program that displays Photon graphical output inside another
windowing system such as the X Window System.
820 Glossary
event
A data structure that represents an interaction between you and an
application or between applications. Events travel through the event
space either toward you or away (i.e. toward the root region).
event compression
The merging of events such that the application sees only their latest
values. The application doesn’t have to process many unnecessary
events.
event handler
A callback function that lets an application respond directly to Photon
events, such as dragging events.
event mask
A set of event types that are of interest to an event handler. When
one of these events occurs, the event handler is invoked.
event space
An abstract, three-dimensional space that contains regions — from
the root region at the back to the graphics region at the front. You sit
outside the event space, looking in from the front. Events travel
through the event space either toward the root region or toward you.
exposure
Typically occurs when a region is destroyed, resized, or moved.
Expose events are sent to applications to inform them when the
contents of their regions need to be redisplayed.
Glossary 821
extent
A rectangle that describes the outermost edges of a widget.
File Manager
The Photon File Manager (PFM), an application used to maintain and
organize files and directories.
focus
A widget that has focus will receive any key events collected by its
window.
focus region
A region placed just behind the device region by the Photon
Window Manager that lets it intercept key events and direct them to
the active window.
focused event
A key or pointer event that has been assigned a location in the Photon
event space. Also called a cooked event.
folder
In the Photon File Manager, a metaphor for a directory.
GC
See graphics context.
geometry negotiation
The process of determining the layout for a widget and its
descendants, which depends on the widget’s layout policy, any size
set for the widget, and the dimensions and desired positions of each of
the widget’s children.
822 Glossary
global header file
A header file that’s included in all code generated by PhAB for an
application. The global header file is specified in PhAB’s Application
Startup Information dialog.
graphics driver
A program that places a region that’s sensitive to draw events on the
user’s side of the device region, collects draw events, and renders the
graphical information on the screen.
Helpviewer
A Photon application for viewing online documentation.
hotkey
A special key or keychord that invokes an action (such as a menu
item) without actually selecting a widget. Also called an accelerator.
Contrast keyboard shortcut.
hotspot
The part of the pointer that corresponds to the coordinates reported
for the pointer (e.g. the intersection of crosshairs, or the tip of the
arrow of the basic pointer).
HSB
Hue-Saturation-Brightness color model.
HSV
Hue-Saturation-Value color model.
Glossary 823
icon module
A PhAB module that associates icons with an application.
image
A rectangular array of color values, where each element represents a
single pixel. See also direct-color and palette-based.
initialization function
In a PhAB application, a function that’s called before any widgets are
created.
input driver
A program that emits, and is the source of, key and/or pointer events.
input group
A set of input and output devices. There’s typically one input group
per user.
instance
A concrete example of an abstract class; for example, “Lassie” is an
instance of the class “dog.” In Photon, an instance is usually a widget
instance; for example, a pushbutton is an instance of the PtButton
widget class. When an instance of a widget is created, the initial
values of its resources are assigned.
instance name
In PhAB, a string that identifies a particular instance of a widget so
that you can access the instance in your application’s code.
824 Glossary
instantiation
The action of creating an instance of a widget class in an application.
internal link
A PhAB mechanism that lets a developer access a PhAB module
directly from an application’s code.
Image Viewer
A Photon application (pv) that displays images.
key modifier
A flag in a key event that indicates the state of the corresponding
modifier key when another key was pressed.
keyboard driver
A program that gets information from the keyboard hardware, builds
Photon key events, and emits them towards the root region.
keyboard shortcut
A key that selects a menu item. The shortcut works only if the menu
is displayed. Contrast hotkey.
language database
A file that contains the text strings used in a PhAB application; a
language database makes it easier to create multilingual applications
with PhAB’s language editor.
link callback
A mechanism that connects different parts of a PhAB application. For
example, a link callback can be invoked to display a dialog when a
button is pressed.
Glossary 825
margin
The area between a widget’s border and canvas.
memory context
A draw context in which Photon draw events are directed to memory
for future displaying on the screen, as opposed to a printer (print
context) or to the screen directly (the default draw context).
menu module
A PhAB module used to create a menu.
method
A function that’s internal to a widget class and invoked under specific
conditions (e.g. to draw the widget). Methods are provided as
pointers to functions in widget class records.
modifier key
A key (such as Shift, Alt, or Ctrl) used to change the meaning of
another key.
module
An object in PhAB that holds an application’s widgets. PhAB
modules include windows, menus, icons, pictures, and dialogs.
mouse driver
A program that gets information from the pointer hardware, builds
Photon raw pointer events, and emits them towards the root region.
opaque
The state of a region with regard to events. If a region is opaque to an
event type, any event of that type that intersects with the region has its
826 Glossary
rectangle set adjusted to clip out the intersecting area. The region
prevents the event from passing through.
palette
An array of colors. A hard palette is in hardware; a soft palette is in
software.
palette-based
A color scheme in which each pixel is represented by an index into a
palette. Contrast direct-color.
PDR
See Press-drag-release.
PFM
See Photon File Manager.
PhAB
Photon Application Builder. Visual design tool that generates the
code required to implement a user interface.
phditto
A utility that accesses the Photon workspace on a remote node. See
also ditto.
Phindows
Photon in Windows. An application that accesses a Photon session
from a Microsoft Windows environment.
Glossary 827
Photon Manager or server
The program that maintains the Photon event space by managing
regions and events.
Photon Terminal
An application (pterm) that emulates a character-mode terminal in a
Photon window.
picture module
A PhAB module that contains an arrangement of widgets that can be
displayed in another widget or used as a widget database.
pixmap
A bitmap or image.
plane mask
A mask used to restrict graphics operations to affect only a subset of
color bits.
point source
A single-point rectangle set used as the source of an event.
pointer
An object on the screen that tracks the position of a pointing device
(e.g. a mouse, tablet, track-ball, or joystick). Photon has several
pointers indicating various states: Basic, Busy, Help, Move, Resize,
I-beam, No-input.
828 Glossary
Press-drag-release (PDR)
A method of selecting a menu item by pressing down a mouse button
while pointing to a menu button, dragging until the desired item is
highlighted, and releasing the mouse button.
print context
A draw context in which Photon draw events are directed to a file, as
opposed to the screen (the default draw context) or to memory
(memory context).
printer driver
A program that converts Photon draw stream format into a format
suitable for a printer, including PostScript, Hewlett-Packard PCL, and
Canon.
procreated widget
A widget created by another widget (as opposed to an application),
such as the PtList and PtText created by a PtComboBox. Also
known as a subordinate child.
pterm
A Photon Terminal; an application that emulates a character-mode
terminal in a Photon window.
pulse
A small message that doesn’t require a reply; used for asynchronous
communication with a Photon application.
pv
See Image Viewer.
PWM
See Photon Window Manager.
Glossary 829
raw event
An input event that hasn’t been assigned a location in the Photon
event space. Also called an unfocused event.
raw callback
A function that lets an application respond directly to Photon events
such as dragging events. Also called an event handler.
realize
To display a widget and its descendants, possibly making them
interactive.
rectangle set
An array of nonoverlapping rectangles associated with an event.
region
A rectangular area within the Photon event space that’s used by an
application for collecting and emitting events.
resize policy
A rule that governs how a widget resizes itself when its contents
change.
resource
An attribute of a widget, such as fill color, dimensions, or a callback
list.
root region
The region at the very back of the Photon event space.
sensitive
The state of a region with regard to events. If a region is sensitive to a
particular type of event, the region’s owner collects a copy of any
such event that intersects with the region.
830 Glossary
setup function
A function that’s called after a PhAB module is created.
shelf
An application that attaches areas to the outside edge of the screen.
You can add plugins to customize these areas, such as a taskbar,
launcher, clock, and magnifier.
Snapshot
A Photon application for capturing images of the screen.
specific placement
The placement of a region when one or more siblings are specified.
The opposite of default placement.
subordinate child
A widget created by another widget (as opposed to an application),
such as the PtList and PtText created by a PtComboBox. Also
known as a procreated widget.
taskbar
A shelf plugin that displays icons representing the applications that
are currently running.
tile
A data structure used to build linked lists of rectangles, such as a list
of the damaged parts of an interface.
Glossary 831
topic path
Help information identified by a string of titles that are separated by
slashes.
topic root
A topic path that’s used as a starting point for locating help topics.
topic tree
A hierarchy of help information.
translation file
A file containing translated strings for a PhAB application. There’s
one translation file per language supported by the application.
unfocused event
See raw event.
Unicode
The ISO/IEC 10646 16-bit encoding scheme for representing the
characters used in most languages.
UTF-8
The encoding for Unicode characters, where each character is
represented by one, two, or three bytes.
widget
A component (e.g. a pushbutton) in a graphical user interface.
widget class
A template for widgets that perform similar functions and provide the
same public interface. For example, PtButton is a widget class.
832 Glossary
widget database
In PhAB, a module containing widgets that can be copied at any time
into a window, dialog, or other container.
widget family
A hierarchy of widget instances. For example, a window and the
widgets it contains.
widget instance
See instance.
Window Manager
See Photon Window Manager.
window module
A PhAB module that’s instantiated as a PtWindow widget.
window region
A region that belongs to an application window.
work procedure
A function that’s invoked when there are no Photon events pending
for an application.
workspace
See console.
workspace menu
A configurable menu that’s displayed when you press or click the
right mouse button while pointing at the background of the desktop.
Glossary 833
Index
! A
.ldb extension 439 AB_ITEM_DIM 345
.wgtd extension 145, 153 AB_ITEM_NORMAL 345
.wgtm extension 145, 155 AB_ITEM_SET 345
.wgtp extension 145, 164 AB_OPTIONS 337
.wgtw extension 145, 152 abapp.dfn 308
/usr/bin/photon 449 abcpal.cfg 214
/usr/photon/lib 24 abdefine.h 327
:: in function names 334 abdefine.h 302
@ in function names 333 abevents.h 302
@ in instance names 171, 436 abimport.h 302, 304
_CS_LIBPATH configuration ABLANG 446
string 768 abLfiles 301
_NTO_CHF_COID_DISCONNECT ablinks.h 302
474 ABLPATH 439, 446, 448, 449
_NTO_CHF_DISCONNECT 474 ABM_... manifests 331, 419
<PhWm.h> 692 abmain.c, abmain.cc 302
<PkKeyDef.h> 361 ABN_... manifests 157, 327, 328,
<Pt.h> 711 345
<utf8.h> 754 About PhAB (Help menu) 94
abplatform 302
ABR_CANCEL 340
ABR_CODE 340
ABR_DONE 340
ABR_POST_REALIZE 339
Index 835
Index
836 Index
Index
Index 837
Index
838 Index
Index
Index 839
Index
840 Index
Index
Index 841
Index
842 Index
Index
Index 843
Index
844 Index
Index
Index 845
Index
F FontName 590
fonts
fd handler 486 available, determining 589
file extension names 588, 590
.ldb 439 proportional, repairing 599
.wgtd 145, 153 symbol metrics 583
.wgti 145 widget resources, editing 216
.wgtm 145, 155 foundry names 588
.wgtp 145, 164 frame, window 734
.wgtw 145, 152 functions
File menu 85 editor 224
Close 85, 128 prototypes 304
Exit 86 potential problems 305
Export Files 86
Import Files 86, 139–141
New 85, 121
Open 85, 95, 123 G
Revert 85
Save 85, 96, 127 Generate Language Database
Save As 85, 126, 127 (Project menu, Language
files Editor submenu) 439
mixed-case names under Generate Report (Project menu) 89
Microsoft Windows 799 Generate UI (Build menu) 90, 301
non-PhAB, including in your geometry
application 320, 321 data types 341
fill attributes 523 negotiation 249
Filter callbacks 245, 675, 683, 716 widget 14
Find (Edit menu) 87, 111 getopt() 337
Fit in Window (View menu) 92 global header file 132, 300, 331
flag editor 215 gradients 567
flickering, reducing 214, 352, 547, application-level 567
550 driver-level 567
flux count 353 image 539
focus 181–183, 382, 730 graphics drivers 22, 649, 730–732,
functions 183 734
region 735 multiple 733
FontDetails 590
846 Index
Index
Index 847
Index
848 Index
Index
Index 849
Index
850 Index
Index
Index 851
Index
O PdGetDevices() 553
PdGetOffscreenContextPtr() 555,
offscreen context 560
(PdOffscreenContext_t) PdIsOffscreenLocked() 561
555 PdLockOffscreen() 561
offscreen locks 560 PdOffscreenContext_t 555
offscreen-context container 550 PDR (press-drag-release)
Open (File menu) 85, 95, 123 method 163
Options menu PdReleaseDirectContext() 553
Preferences 88, 104 PdSetOffscreenTranslation() 555
options, command-line See PdSetTargetDevice() 553, 556, 565
command-line options PdUnlockOffscreen() 561
Outbound callbacks 679 PfExtentTextCharPositions() 599
overlapping polygons 530 PfGenerateFontName() 590
Pg_ARC 532
Pg_ARC_CHORD 532
Pg_ARC_PIE 532
P Pg_BACK_FILL 535, 537
Pg_CLOSED 529
palette-based image See images
Pg_CM_PRGB 519
palettes (View menu) 198
Pg_DRAW_FILL 525, 527
parallel operations 491
Pg_DRAW_FILL_STROKE 525,
parent region 653–655
Paste (Edit menu) 87, 96, 191 527
Pg_DRAW_STROKE 525, 527
PATH environment variable 768
Pg_DRIVER_STARTED 560
pathname delimiter in QNX
Pg_ENTERED_DIRECT 560
Momentics documentation
Pg_EXITED_DIRECT 560
xliv
Pg_IMAGE_DIRECT_888 551
PC See print contexts
Pg_IMAGE_PALETTE_BYTE 551
PdCreateDirectContext() 553
Pg_LAYER_ARG_LIST_BEGIN
PdCreateOffscreenContext() 555,
556 576
Pg_LAYER_ARG_LIST_END 576
PdCreateOffscreenLock() 560
Pg_POLY_RELATIVE 529
PdDestroyOffscreenLock() 561
Pg_VIDEO_MODE_SWITCHED
PdDirectContext_t 552
PdDirectStart() 553 559
PdDirectStop() 553 PgAlphaOff() 562
PdDupOffscreenContext() 555 PgAlphaOn() 562
852 Index
Index
Index 853
Index
854 Index
Index
Index 855
Index
856 Index
Index
Index 857
Index
858 Index
Index
Index 859
Index
Pt_ARG_ANCHOR_OFFSETS Pt_ARG_MARGIN_HEIGHT 16
266 Pt_ARG_MARGIN_WIDTH 16
Pt_ARG_AREA 17, 104 Pt_ARG_MENU_FLAGS 349
Pt_ARG_BASIC_FLAGS 15 Pt_ARG_POINTER 363, 517
Pt_ARG_BEVEL_WIDTH 15, Pt_ARG_POS 17, 104, 255, 684
360 Pt_ARG_RAW_CALC_OPAQUE_F
Pt_ARG_BUTTON_TYPE 348 512
Pt_ARG_CBOX_FLAGS 362 Pt_ARG_RAW_CONNECT_F
Pt_ARG_CONTAINER_FLAGS 512
182, 240, 243 Pt_ARG_RAW_DRAW_F 512
Pt_ARG_CURSOR_OVERRIDE Pt_ARG_RAW_EXTENT_F 512
374 Pt_ARG_RAW_INIT_F 512
Pt_ARG_DIM 15, 104, 188, 255 Pt_ARG_RESIZE_FLAGS 250,
Pt_ARG_EFLAGS 455 251, 254
Pt_ARG_EXTENT 17, 104 PT_ARG_ROW_LAYOUT_DATA
PT_ARG_FILL_LAYOUT_INFO editing 229
editing 226 PT_ARG_ROW_LAYOUT_INFO
Pt_ARG_FLAGS 15, 182, 240, editing 227
244, 369, 491, 625, 629, Pt_ARG_STYLE 396
677, 679, 696, 715 Pt_ARG_TERM_ANSI_PROTOCOL
PT_ARG_GRID_LAYOUT_DATA 364
editing 230 Pt_ARG_TEXT_STRING 360,
PT_ARG_GRID_LAYOUT_INFO 370
editing 228 Pt_ARG_TIMER_INITIAL 343,
Pt_ARG_GROUP_FLAGS 259 548
Pt_ARG_GROUP_ORIENTATION Pt_ARG_TIMER_REPEAT 343,
257 548
Pt_ARG_GROUP_ROWS_COLS Pt_ARG_USER_DATA 361, 517
258 Pt_ARG_WINDOW_HELP_ROOT
Pt_ARG_GROUP_SPACING 257, 454
259 Pt_ARG_WINDOW_MANAGED_FLAGS
Pt_ARG_HELP_TOPIC 455 454, 505, 689, 691, 692,
Pt_ARG_ITEMS 362 695, 696, 699
Pt_ARG_LABEL_IMAGE 361, Pt_ARG_WINDOW_NOTIFY_FLAGS
541, 544, 547 689, 692, 694, 695
Pt_ARG_LABEL_TYPE 141, 541, Pt_ARG_WINDOW_RENDER_FLAGS
544, 547 454, 689, 690, 700
860 Index
Index
Index 861
Index
862 Index
Index
Index 863
Index
864 Index
Index
Index 865
Index
866 Index
Index
Index 867
Index
868 Index
Index
Index 869
Index
870 Index
Index
Index 871
Index
872 Index
Index
Index 873
Index
874 Index
Index
Send To Back 93 X
Show* 94
windows x86, support for 24
Alt combinations, passing to XBM images
application 696 importing 140
backdrop 696
blocking 696, 701
closing, verifying 694
focus, giving 696 Z
frame
zip 799
buttons 690
Zoom (View menu) 92
region 661, 734, 735
Zoom Mode (Project menu) 88, 97
manifest 329
maximizing 696
minimizing 696
module 152, 689
placing in front 696
region 734, 735
resizing 153
services 734
startup 129, 131, 132
tutorial 72
Windows (Microsoft), running PhAB
on 797
Winzip 799
work area 33, 149
work procedures 491, 493
function type
(PtWorkProc_t) 494
ID (PtWorkProcId_t) 494
threads and 507
workspace
menu 735
region 651, 735
Index 875