home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
-
- Welcome to EDM/2 - The Electronic OS/2 Developers' Magazine!.
-
- Portions Copyright (C)1993 by Steve Luzynski.
-
- Issue #1 - March 1993.
-
- (Press 'Forward' to page.)
-
-
- ΓòÉΓòÉΓòÉ 2. Copyright Notice and other Legal Stuff ΓòÉΓòÉΓòÉ
-
- EDM/2 Copyright (C)1993 by Steve Luzynski. This publication may be freely
- distributed in electronic form provided that all parts are present in their
- original unmodified form. A reasonable fee may be charged for the physical act
- of distribution; no fee may be charged for the publication itself.
-
- All articles are copyrighted by their authors. No part of any article may be
- reproduced without permission from the original author.
-
- Neither this publication nor Steve Luzynski is affiliated with International
- Business Machines Corporation.
-
- OS/2 is a registered trademark of International Business Machines Corporation.
- Other trademarks are property of their respective owners. Any mention of a
- product in this publication does not constitute an endorsement or affiliation
- unless specifically stated in the text.
-
-
- ΓòÉΓòÉΓòÉ 3. From the Editor ΓòÉΓòÉΓòÉ
-
- Hello, and welcome to the first issue of EDM/2, the Electronic Developer's
- Magazine!
-
- This project was the result of my own struggle to get information on
- programming OS/2, particularly in the case of writing Presentation Manager
- programs. What information there is available seems to be spread out amongst
- .INF files, books written for OS/2 1.3, and old magazine articles. My goal was
- to provide a single resource where developers, both new and old, could get
- information on topics ranging from "How do I create a window?" to "How do I
- write an IFS?"
-
- The tone of this magazine may be a bit irreverent at times - not all computer
- programmers are antisocial hermits who only leave their rooms to get more
- Doritos (that's just me). Consider yourself warned. (insert smile here)
-
- In later issues, I'll use this space to actually talk about topics related to
- developing for OS/2. This time, however, I have some administrative business to
- get off my chest...
-
- Contributions
-
- If you have an idea for an article, by all means contact me! Since this
- magazine is targeted at developers of all levels, no topic is too general or
- too esoteric to be appreciated by someone. You can generally assume basic
- familiarity with the C programming language - beyond that, use your judgement.
- Someone reading an article on writing device drivers will probably be more
- advanced than someone reading about resource files.
-
- One area I would like to see covered that hasn't been in this issue is the
- commercial developer's software that is available out there. Reviews of Zortech
- C++, IBM C Set/2, etc. would make nice inclusions.
-
- Money
-
- This magazine is free for the taking. All of the material is copyrighted by the
- person who wrote it - the authors retain all rights to the text as well as
- their code unless stated otherwise. I can't pay them for their efforts, so by
- leaving their work as theirs I leave open the possibility for paid reprints in
- traditional paper works.
-
- Since I really could in no way control the random distribution of this work,
- I'm not even attempting to charge for it. However, if you do feel a deep seated
- desire to pay someone for it, by all means feel free to send your donation:
-
- o to me. Your money will all be pushed back into the magazine in the form of
- storage space, Compuserve fees, etc. My address can be located by emailing me
- at the address listed at the end of this article.
-
- o to the Free Software Foundation. They are responsible for emx/gcc and a lot
- more really nice FREE packages available for a variety of machines. They also
- have administrative overhead just like any other organization. They can be
- reached at:
-
- Free Software Foundation, Inc.
- 675 Mass Ave
- Cambridge, MA 02139, USA
-
- Etc
-
- This articles presented in this magazine were written by people with a wide
- variety of expertise in the use of IPF (the Information Presentation Facility).
- Because of this, the style of the articles is different for each one - from a
- straightforward scrolling window to multilevel windows with custom icons and
- hotlinks to glossary items. As we all get better at this, eventually everything
- will look as nice as Gavin's article. (editorial smile) Please bear with me
- until then and let me know if you have any suggestions on style or format. But
- enough from me. Let's get to it.
-
- -Steve Luzynski, Editor.
-
- sal8@po.cwru.edu
-
-
- ΓòÉΓòÉΓòÉ 4. This Month's Features ΓòÉΓòÉΓòÉ
-
- o Getting Started with emx/gcc
-
- o The Making of MineSweeper
-
- o Palette Manager
-
- o Advanced GPI:Retained Segments and Transformations
-
-
- ΓòÉΓòÉΓòÉ 4.1. Getting Started with emx/gcc ΓòÉΓòÉΓòÉ
-
- Installing EMX
-
- A Free 'C' Compiler
- for OS/2 2.0.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- (Note: This document was originally written by Brooke P. Anderson. It has been
- modified slightly to conform to the .INF format. The original may be obtained
- from the same source as emx itself.)
-
- Introduction
-
- This document describes a free GNU software-development system for OS/2 v2.0.
- It tells you how to acquire all of the necessary software, how to install it,
- how to start using it, and where to look for more information.
-
- The GNU software-development system includes a C and C++ compiler, a debugger,
- an assembler, a make utility (for automating the compilation of programs made
- of many source files), and a hypertext reader (for reading the documentation).
- The compiler generates full 32-bit, optimized code, and it supports all of the
- OS/2 API calls, so you software developers out there can use it for PM
- programming, manipulating semaphores, manipulating threads, using named pipes,
- etc. Optional packages include curses (a standard library for manipulating
- screenfuls of text and for moving the cursor), a collection of sample programs,
- and full source code for the system.
-
- GNU software is originally developed by the Free Software Foundation, an
- organization which produces a lot of free software for UNIX. After the Free
- Software Foundation releases the UNIX versions, people often port them to many
- other operation systems (such as OS/2). Despite the fact that the software is
- free, the UNIX community considers it a standard and often prefers it over
- other products because of its high quality. The compilers, for example,
- produce well-optimized code.
-
- Sometimes, there is more than one port of a GNU program. For OS/2, there are
- two different ports of the GNU compiler (called "gcc"). This document
- discusses only one of them (the EMX port) since the EMX port provides faster
- floating point routines and works with a debugger. This document deals with
- version 0.8f of the EMX port, which is the latest version. People frequently
- produce new versions of and enhancements for the GNU software and the ports
- based on it.
-
- IMPORTANT: I have not tested the software on FAT file systems. I think it
- will work, but I have not checked all of the file names to make sure they
- comply with the egregious 8.3 file-naming convention.
-
- The following topics may be selected by clicking on the individual topics of
- interest or by simply pressing the 'Forward' button on the bottom of the
- window.
-
- How to get emx
- How to install emx
- UNZ50x32.EXE
- WHLINF8F.ZIP
- GNUMK362.ZIP
- GNUDEV.ZIP, EMXDEV.ZIP, GPPDEV.ZIP, and OBJCDEV.ZIP
- The final steps
- Using info
- Using the compiler
- Using the debugger
- Using make
- Using the assembler
- Where to get more information
- Conclusions
- Optional packages
- Sources of distribution
- Distributing this document
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.1. How to get emx ΓòÉΓòÉΓòÉ
-
- The full software-development system (and the various optional packages
- described later in this document) are available from a variety of sources. If
- you have access to Internet, you can get the files from anonymous-ftp sites.
- In the USA, the main anonymous-ftp site for OS/2 is ftp-os2.nmsu.edu. In
- Germany, the main anonymous-ftp site for OS/2 is rusinfo.rus.uni-stuttgart.de.
- Also, see the end of this document for a list of people who are willing to
- distribute the whole system through regular mail.
-
- You need to obtain the following files (the sizes of the files are listed next
- to the names): emxdev.zip (620k), gnudev.zip (873k), gppdev.zip (944k),
- gobjcdev.zip (426k), gnumk362.zip (255k), whlinf8f.zip (814k), and unz50x32.exe
- (114k). This file (emxst31.doc) is available in emxst31.zip (10k).
-
- On ftp-os2.nmsu.edu, these files are available in
- pub/os2/2.0/programming/emx-0.8f -- except for emxst31.zip, whlinf8f.zip, and
- the correct version of gnumk362.zip, which are temporarily in pub/uploads. On
- rusinfo.rus.uni-stuttgart.de, check in pub/os2/emx-0.8f.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.2. How to install it ΓòÉΓòÉΓòÉ
-
- The following subsections describe how to install the various pieces of the
- GNU software-development system. Go through the procedures step by step as
- described -- the order is important. Don't feel compelled to read through any
- of the readme files or other documentation spit out during the unarchiving
- process -- I think you will have a much easier time if you go through this
- document beforehand.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.3. UNZ50X32.EXE ΓòÉΓòÉΓòÉ
-
- You will need unzip v5.0 to unarchive the files. Earlier versions of unzip
- (including PKZip versions earlier than 1.9) will not work.
-
- To unarchive unz50x32.exe, you simply move it to a convenient directory and
- type "unz50x32". Add the name of the directory unzip is in to your path. For
- example, I have unzip in c:\apps\unzip, so I appended "c:\apps\unzip;" to the
- "SET PATH" statement in my config.sys file. Then reboot. Now, you can
- unarchive any zip archive by typing "unzip filename", where "filename.zip" is
- the name of the archive.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.4. WHLINF8F.ZIP ΓòÉΓòÉΓòÉ
-
- Copy whlinf8f.zip to any convenient directory and unarchive it. It will
- disgorge a program called "info.exe" (the hypertext reader), several auxiliary
- files, and a bunch of documentation files for itself, the compiler, the
- debugger, and make.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.5. GNUMK362.ZIP ΓòÉΓòÉΓòÉ
-
- Copy gnumk362.zip to any convenient directory and unarchive it. The archive
- will create the directory ".\make" and disgorge several files into it. In
- other words, if you unarchive gnumk362.zip in the directory "\apps", the
- process will create the directory "\apps\make". You can delete the info
- directory (the one in the make directory) as its contents are duplicated in
- whlinf8f.zip.
-
- Add the name of the directory make is in to your path statement in config.sys.
- (I have make in c:\apps\make, so I appended "c:\apps\make;" to the "SET PATH"
- statement in my config.sys.)
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.6. GNUDEV.ZIP, EMXDEV.ZIP, GPPDEV.ZIP, and OBJCDEV.ZIP ΓòÉΓòÉΓòÉ
-
- These archives create the directory ".\emx" and a bunch of directories under
- that. Thus, unarchive the files from the directory in which you want this emx
- directory. (In other words, if you unarchive gnudev.zip in the directory
- "\apps", the process will create \apps\emx, \apps\emx\bin, \apps\emx\lib, and
- many other such directories.)
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.7. The final steps ΓòÉΓòÉΓòÉ
-
- Now, you need to modify your config.sys file. The examples below are from my
- config.sys file, and I have the emx directory installed under c:\apps. Thus,
- my system has the directories "c:\apps\emx\dll", "c:\apps\emx\lib",
- "c:\apps\emx\include", "c:\apps\emx\bin", and so on -- you will need to modify
- the following examples so that the directories are specified correctly.
-
- First, you need to specify in your libpath the location of emx.dll and other
- dll files. In my config.sys, I have
-
- LIBPATH=.;C:\OS2\DLL;C:\OS2\MDOS;C:\; C:\OS2\APPS\DLL;c:\apps\emx\dll;
-
- Second, add the name of the emx\bin directory to your path statement in
- config.sys. (I appended "c:\apps\emx\bin;" to the "SET PATH" statement in my
- config.sys.)
-
- Third, you need to set a few environmental variables so that the compiler
- knows where to find and how to use various files:
-
- set C_INCLUDE_PATH=c:/apps/emx/include
- set LIBRARY_PATH=c:/apps/emx/lib
- set CPLUS_INCLUDE_PATH=C:/apps/emx/include.cpp;C:/apps/emx/include
- set PROTODIR=c:/apps/emx/include.cpp/gen
- set OBJC_INCLUDE_PATH=c:/apps/emx/include
- set TERM=mono
- set TERMCAP=c:/apps/emx/etc/termcap.dat
-
- IMPORTANT: you need to use forward slashes ("/") and not backward slashes
- ("\") when setting these environmental variables. However, do NOT use forward
- slashes in your libpath statement.
-
- Now, reboot your machine so that these definitions take effect. Then, go into
- an OS/2 window and type "cd \apps\emx\lib" (or whatever you would type to get
- to emx\lib), and type "omflib". This completes the installation process.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.8. Using info ΓòÉΓòÉΓòÉ
-
- Go into the directory in which info.exe resides. Type "info". You are now
- looking at a screen that has some information on the top half (information such
- as "Typing 'd' returns here, 'q' quits, '?' lists all info commands, 'h' gives
- a primer for first-timers . . .") and a list of subjects near the bottom
- (subjects such as "Info", "Make," "Gcc", "Gdb", etc.).
-
- Go ahead and type "h" for a tutorial. The most basic functions to remember
- are: type "q" to quit, type "?" to get a list of commands, hit the space bar
- or PgDn key to go down a page, press the Del key or the PgUp key to go up a
- page, press "n" to go to the next node (think of a node as being a collection
- of one or more pages dealing with a single topic), press "p" to go to the
- previous node, use the up and down arrow keys to highlight choices of new nodes
- to jump to, and press enter to jump to the highlighted node.
-
- Just play around with it a little while, and you will get the hang of it.
- Type "d" to get back to the main directory, use the down arrow to highlight
- "Gcc", press enter, press the down arrow to highlight "Contributors", press
- enter, scan through the pages of text by hitting the PgDn key a couple of
- times, type "?" to see a list of commands, type "p" a couple of times, etc.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.9. Using the compiler ΓòÉΓòÉΓòÉ
-
- To compile a C source file called "myprog.c", type "gcc -o myprog.exe
- myprog.c". The -o switch tells gcc that it should call the resulting
- executable "myprog.exe". To compile the C++ source file "myprog.cc", type "gcc
- -o myprog.exe myprog.cc -lgpp". C++ source files should have the extension
- ".cc". The -lgpp switch tells gcc to link the C++ libraries. You can also
- tell gcc to optimize your code by using the -O switch. There are two levels of
- optimization (-O and -O2, the highest being -O2). Thus, for the
- fastest-executing code (at the expense of time to compile and of size of the
- executable), you would type "gcc -O2 -o myprog.exe myprog.c" for myprog.c and
- "gcc -O2 -o myprog.exe myprog.cc -lgpp" for myprog.cc.
-
- Note: Specifying "-o myprog.exe" is important. If you don't specify ".exe"
- as the suffix of the output file name, the compiler will generate a UNIX-style
- executable which will not run under OS/2 even if you subsequently rename the
- file so that it has a .exe extension.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.10. Using the debugger ΓòÉΓòÉΓòÉ
-
- To debug a program, you need to compile it with the -g switch: "gcc -g -o
- myprog.exe myprog.c" for myprog.c and likewise for myprog.cc. After compiling,
- you then type "gdb myprog.exe" to start the debugger. Type "h" at the debugger
- prompt to get a list of help topics. gdb supports all sorts of breakpoints
- (including conditional ones), and you can watch variables, set variables to
- different values, etc. For example, to get help on running programs from
- within the gdb, you can type "help running". That will give you a list of
- commands such as run, kill, step, etc. You can get help on individual commands
- by typing, for example, "help step".
-
- The following is a sample compile and debug session. Go into the emx\test
- directory and type "gcc -g -o hello.exe hello.cc -lgpp" to compile hello.cc.
- Type "hello" to run it, to see if the compiler is functioning. The program
- will print "Hello, world!" on the screen. Now type "gdb hello.exe" to start
- gdb. At the prompt "(gdb)", type "list main" to list the function "main". Then
- type "break 5" to cause execution to stop at line 5 in the main function. Type
- "run" to start execution -- it will stop at line 5. Type "print argc" to see
- the value of the variable "argc". Type "step" to run line 5 then halt
- execution at the next line. Type "quit" to quit. To list a function (main(),
- say) that is longer than a screenful, type "list main"; then type "list" again
- to list the next screenful of main.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.11. Using make ΓòÉΓòÉΓòÉ
-
- Assume you have a program made of the following source files: "myprog1.c" and
- "myprog2.c". You might manually compile these files by typing "gcc -o
- myprog.exe myprog1.c myprog2.c". Of course, if you change only one of the
- files, typing such a command causes the recompiling of both source files.
- During development, this could be tiresome if the files take a long time to
- compile. A better way would be to type "gcc -c myprog1.c" which compiles
- myprog1.c into the object file "myprog1.o" (the -c switch tells gcc to make an
- object file), then to type "gcc -c myprog2.c" to generate myprog2.o, and
- finally to type "gcc -o myprog.exe myprog1.o myprog2.o". This way, if you
- change only myprog1.c, you can recompile it and relink it with myprog2.o to
- create myprog.exe (skipping the "gcc -c myprog2.c" step). This will be much
- faster if the source files take a long time to compile (or if you have a lot of
- source files).
-
- Of course, doing all this typing is tiresome, too. Also, if myprog1.c happens
- to depend on myprog1.h, and you change myprog1.h, you must recompile myprog1.c.
- Thus, you have to keep track of all the file dependencies in order to know,
- after changing one header file, which other files need to be recompiled.
-
- Fortunately, make takes care of all of this automatically. All you have to do
- is create one text file that describes the various dependencies and the various
- steps to compile the program. You name the text file "Makefile". From then
- on, whenever you type "make", make examines the Makefile, looks for files which
- have changed since the last compile, recompiles any files which depend on the
- changed files, and relinks everything into a new executable.
-
- For example, suppose that myprog.exe is made from myprog1.c and myprog2.c,
- that myprog1.c contains the lines "#include "myprog1.c"" and "#include
- "mainhead.h"", and that myprog2.c includes myprog2.h and mainhead.h. The
- Makefile describing all of this is
-
- myprog.exe: myprog1.o myprog2.o
- gcc -o myprog.exe myprog1.o myprog2.o
-
- myprog1.o: myprog1.c myprog1.h mainhead.h
- gcc -c myprog1.c
-
- myprog2.o: myprog2.c myprog2.h mainhead.h
- gcc -c myprog2.c
-
- The first line shows that myprog.exe depends on myprog1.o and myprog2.o. If
- either of those has changed since the last time make was invoked, make will
- relink them to create myprog.exe by giving the command under the first line.
- The fourth line shows that myprog1.o depends on myprog1.c, myprog1.h, and
- mainhead.h. If any of these three files have changed since the last time make
- was run, make will recompile myprog1.o by issuing the command on line five. It
- will also realize that myprog.o has changed, that myprog.exe depends on
- myprog.o, and will relink myprog.exe. If mainhead.h is changed, make will
- recompile and relink everything since myprog1.o needs to be changed, myprog2.o
- needs to be changed, and thus myprog.exe needs to be changed.
-
- The example above shows the general form of a Makefile. You give a target
- (like "myprog.exe" or "myprog1.o") followed by a colon, followed by a space,
- followed by a space-delimited list of files the target depends on. The next
- line specifies the action to be taken when any of the dependencies change: the
- first character MUST be a tab (not just a bunch of spaces used for
- indentation); then you type the command make should issue. A Makefile is just
- a list of such targets, dependencies, and actions.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.12. Using the assembler ΓòÉΓòÉΓòÉ
-
- The compiler provided with this system (gcc) can handle not only C and C++
- code but assembly as well. It can also generate assembly language from C or
- C++ source. To assemble and link assembly-language programs, type "gcc -o
- myprog.exe myprog.s" where "myprog.exe" is the name of the executable and where
- "myprog.s" is the name of the source-code file. Assembly-language source-code
- files should have the extension ".s". To generate assembly from C code, type
- "gcc -S myprog.c" where "myprog.c" is the name of the C-source-code file -- the
- resulting assembly language will be put into the file "myprog.s".
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.13. Where to get more information ΓòÉΓòÉΓòÉ
-
- The GNU software-development system does not come with documentation like that
- you would get with, for example, Borland C++. However, emxdev.doc (in the
- emx\doc directory) does contain a complete list of library functions, including
- a list of headers you need to include, what the functions do, and what
- parameters they accept.
-
- Also, since the C compiler is ANSI-C compliant (or at least close to it) and
- since the C++ compiler is close to AT&T-C++-2.0 compliant, you can use just
- about any reference manuals for ANSI C and AT&T C++ 2.0. I use the ones I got
- with an old version of Borland Turbo C++. If you don't have such manuals, you
- should be able to find something suitable in a bookstore. If you want a C
- reference manual, I recommend C: A REFERENCE MANUAL, by S. P. Harbison and G.
- L. Steele, Jr. (Prentice-Hall, 1991). If you are just learning C or C++, there
- is a large number of books to choose from, and you shouldn't have any trouble
- finding one that is suitable.
-
- For those of you developing applications that use the PM or that use special
- OS/2 functions, the system DOES support all of the OS/2 API functions,
- including ones for semaphores, PM programming, named pipes, threads, etc., and
- it supports Kbd, Mou, and Vio functions. See emxdev.doc (in the emx\doc
- directory) for a list of the supported functions. The documentation does not
- contain a manual on how to use these API calls -- you need an OS/2 programming
- book for that. For information on programming the PM, take a look at:
-
- o OS/2 2.0 Presentation Manager GPI: A Programming Guide to Text, Graphics, and
- Printing, by G. C. E. Winn (Van Norstrand Reinhold, 1992)
-
- o Learning to Program OS/2 2.0 Presentation Manager by Example: Putting the
- Pieces Together, by Stephen Knight (Van Norstrand Reinhold, 1992).
-
- o Programming the OS/2 Presentation Manager, by Charles Petzhold (Microsoft
- Press, 1989).
-
- The book by Petzhold was written for OS/2 1.3, but the information in it is
- still valid. You can also get the IBM redbooks (which are quite economical and
- of which there is a large assortment of titles).
-
- For using assembly language, the best choice would be a book about
- assembly-language programming in OS/2, supplemented perhaps with a book on
- programming the 80386 or 80486.
-
- Also, way back when you were unarchiving, you might have been itching to
- examine the various readme files and other documentation. Now is the time to
- do that to your heart's content. Browse through the files in the emx/doc
- directory and the information available from the hypertext reader.
-
- Additional sources of information include GEnie (the OS/2 category of the
- IBMPC bulletin board), Usenet news (the comp.os.os2 newsgroups), and
- CompuServe. These are places where you can exchange information with other
- people who use and program for OS/2. In particular, CompuServe is one of the
- official homes for the OS/2 developers' assistance program. If you are a
- member of the program, IBM will (for only $15) provide you with a CD that
- contains a beta version of the software development kit (including the C and
- C++ compiler and debugger and a full set of on-line documentation), a beta
- version of OS/2 (which contains enhancements such as Windows 3.1
- compatibility), and many other goodies. For more information, type "go os2dap"
- on CompuServe or call 1-407-982-6408. The people at 1-800-3-ibm-os2 might also
- be able to provide more information.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.14. Conclusions ΓòÉΓòÉΓòÉ
-
- I wrote this to help people get started with a free -- yet powerful -- 32-bit
- software-development system for OS/2. For (at most) the price of a few books,
- you have a full programming system for C++, C, and assembly language. For the
- additional price of an OS/2 programming book, you have a bargain-basement SDK.
-
- If you find errors in this document, or if you have suggestions for its
- improvement, please let me know. My GEnie address is "BROOKE", and my Internet
- address is "brooke@hope.caltech.edu".
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.15. Optional Packages ΓòÉΓòÉΓòÉ
-
- There are three optional packages you can get for this software-development
- system, packages which are not necessary but which can nevertheless be
- important. All of them are in the pub/os2/2.0/programming/emx-0.8f directory
- on ftp-os2.nmsu.edu. On rusinfo.rus.uni-stuttgart.de, they are probably
- available in a directory such as pub/os2/emx-0.8f.
-
- The first package contains curses. Curses is a library of functions that
- allow you to move the cursor around on the screen, manipulate screenfuls of
- text, and get input. You need the files "bsddev.zip" and "bsddoc.zip". The
- source code is available in "bsdsrc.zip".
-
- The second package contains full source code to the software-development
- system. Most people do not need the source code to everything, but the source
- code to the libraries (i.e., to all the functions) is sometimes useful. The
- source code to the C libraries is in "emxlib.zip", and the source to the C++
- libraries is in "gccsrc.zip". install.doc (in the emx\doc directory) gives the
- names of all the other relevant archives.
-
- The third package is a collection of sample and test programs that you can use
- to test the compiler and to learn about various aspects of programming. These
- programs are in the file "emxtest.zip."
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.16. Sources of Distribution ΓòÉΓòÉΓòÉ
-
- This document already described some places from which you can get the
- necessary archives: ftp-os2.nmsu.edu and rusinfo.rus.uni-stuttgart.de.
- However, some people don't have access to these sites or don't have modems fast
- enough to download megabytes of data in a reasonable amount of time. For these
- people, I am including the following list of people who are willing to
- distribute the whole system through regular mail. Keep in mind that people
- might change their prices, cease distributing the software, move, etc., so
- contact them first to get current details. Make sure you ask them if they have
- the latest version of the EMX port (which is currently version 0.8f).
-
- Brooke Anderson
- 1155 E. Del Mar #312
- Pasadena, CA 91106
- USA
- Phone: (818) 577-7555
- GEnie: BROOKE
- Internet: brooke@hope.caltech.edu
- Cost: $18 in US; in other countries, shipping + US$10
- Provides: the basic set of archives and bsddev.zip, bsddoc.zip,
- bsdsrc.zip, emxtest.zip, emxlib.zip, and gccsrc.zip.
-
- Juergen Egeling
- Werderstr. 41
- 7500 Karlsruhe
- Germany
- Phone: 0721-373842
- FAX: 0721-373842
- BITNET: ry90@dkauni2
- Internet: ry90@ibm3090.rz.uni-karlsruhe.dbp.de
- X.400: S=ry90;OU=ibm3090;OU=rz;P=uni-karlsruhe;A=dbp;C=de
- Cost: disks + shipping + DM 25
-
- Wey J. Ho
- Department of Physics
- Monash University
- Clayton
- VIC 3168
- Australia
- Phone: +613-565-3615 (or Australia (03) 565 3615)
- Fax: +613-565-3637 (or Australia (03) 565 3637)
- Internet: sci240s@monu6.cc.monash.edu.au
- Cost: disks + shipping + AU$10
-
- Doug Robison
- 1311 Webster
- Chillicothe, MO 64601
- USA
- Phone: (816) 646-1085
- GEnie: D.ROBISON
- Cost: disks + shipping + US$5
-
- If you would like to get on this list and if you have access to Internet or an
- on-line service, just send me your name, a description of how people can
- contact you (including your e-mail address), how much money you want for the
- job (such as "$20", "disks + shipping + $30", or whatever you want to charge),
- and what you are offering for that price. It is helpful to have a list that
- includes people in various countries.
-
-
- ΓòÉΓòÉΓòÉ 4.1.1.17. Distributing this Document ΓòÉΓòÉΓòÉ
-
- I give permission to use, to distribute, and to copy this document freely. If
- you want to upload it to any bulletin-board or on-line service, please do so.
- I do update this document occasionally (and put the latest version on GEnie and
- ftp-os2.nmsu.edu), so you might want to make sure you have the latest version
- before distributing it.
-
- Brooke Anderson
- 1155 E. Del Mar #312
- Pasadena, CA 91106
- USA
- Phone: (818) 577-7555
- GEnie: BROOKE
- Internet: brooke@hope.caltech.edu
-
-
- ΓòÉΓòÉΓòÉ 4.2. The Making of MineSweeper ΓòÉΓòÉΓòÉ
-
- The Making of
- MineSweeper
-
-
- ΓòÉΓòÉΓòÉ 4.2.1. Introduction ΓòÉΓòÉΓòÉ
-
- This document and its contents are copyright (C) 1993, by David Charlap.
-
- This document describes some of the design decisions and problems that I
- encountered when making my MineSweeper for OS/2 program. It assumes a basic
- knowledge of C and Presentation Manager.
-
- I decided to write an OS/2 version of MineSweeper for many various reasons.
- The main reason was to learn a bit more about programming in the OS/2
- presentation manager environment. I also wanted an OS/2 version of this game,
- since loading the Windows environment is a slow procedure. Although there is
- already a version of MineSweeper for OS/2 available, I found it to be lacking
- features of the Microsoft game that I wanted.
-
- I chose to develop MineSweeper using the GCC/2 compiler, since I already had it
- installed on my computer at home. The code I have written should compile
- without change on IBM's C Set/2 compiler or on the EMX/GCC compiler or on any
- other OS/2-compatible compiler.
-
-
- ΓòÉΓòÉΓòÉ 4.2.2. The game ΓòÉΓòÉΓòÉ
-
- Before discussing the design, allow me to explain the basic concept of the
- game.
-
- MineSweeper is a game of logic, pitting the player against a clock. A grid of
- squares is presented, some of which contain mines. All the squares are covered
- at the game's outset. The object is to discover which covered squares contain
- mines under them using clues provided by the program.
-
- A player uncovers a square by clicking on it with the mouse. If the square
- contains a mine, the player loses and play stops. If it does not contain a
- mine, a number is displayed on the square which indicates the number of mines
- that are adjacent to that square. When all the squares that do not contain
- mines are uncovered, the player wins and play stops.
-
- To prevent mistakes, a player may place a flag on a covered square to indicate
- a suspected mine position. The computer will not allow a flagged square to be
- uncovered. The player may also place a question mark on a square to aid in
- visualizing the problem. The computer will allow a question-marked square to
- be uncovered.
-
- The game is timed.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3. The implementation ΓòÉΓòÉΓòÉ
-
- While there are many ways to implement such a game, I chose to copy the
- implementation that Microsoft used for their game. Partly because I was
- already familiar with it, and partly because it is an intuitive approach to the
- game.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3.1. How the game should behave ΓòÉΓòÉΓòÉ
-
- The game is played in a window whose size is directly proportional to the size
- of the game grid. When the grid's dimensions are changed, the window is
- re-sized to accomodate the new grid. The top of the window contains the
- current elapsed time, a count of the number of mines left to be flagged, and a
- button that may be clicked for a quick restart of the game. Below this is the
- game grid.
-
- Covered squares are drawn in a way to resemble buttons.
-
- A user uncovers a square by clicking on it with mouse button 1. When the button
- is depressed over a covered square, the square is drawn as a depressed button.
- If the mouse is dragged over the game grid, the "depressed" button will follow
- the pointer. When the button is released, the square beneath it will be
- uncovered.
-
- If an uncovered square has no adjacent mines, the program will automatically
- uncover all squares adjacent to it.
-
- Flags and question marks are placed by clicking on covered squares with mouse
- button 2. The marks are placed as soon as the button is pressed.
-
- As a shortcut, and an aid to solving, mouse button 3 is used to uncover all the
- squares around an uncovered square. When button 3 is clicked on an uncovered
- square, and there are sufficient flags surrounding the square to satisfy the
- number shown, all remaining squares will be uncovered. For two-button mouse
- users, pressing buttons 1 and 2 together (chord) will also cause this effect.
-
- When the shortcut button is pressed, the squares surrounding the pointer will
- be drawn in the depressed position. As the mouse is dragged, the 3x3 area of
- depressed squares will track the mouse. When button 3 (or either button 1 or
- 2, if the chord is used) is released, the shortcut process will begin.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3.2. How to code this ΓòÉΓòÉΓòÉ
-
- Coding this game play was not simple. My first attempt involved creating
- separate buttons for each square on the playfield. This way, the user could
- just click on a button, and the program would receive a WM_COMMAND message
- indicating which button was clicked.
-
- This proved difficult for many reasons. First, it is hard to get a bitmap
- image on the face of a button. Although I have seen it done, I do not know
- how, and I did not want to take the time to learn. Second, and more important,
- buttons to not behave properly. Ordinary buttons revert to their "up" state if
- the mouse moves off of them while the mouse button is depressed, but there is
- no facility provided to then depress the adjacent button that the pointer is
- subsequently above. Also, facilities are not provided to have more than one
- button draw itself on the "down" state when one is clicked on.
-
- In other words, getting the depressed button images to follow the mouse as it
- is dragged across the playfield is difficult to do, if not impossible, using
- button windows.
-
- Instead, I decided to fake it. I would have multiple bitmaps in memory. One
- for each possible image that could be drawn on a grid square. I would then
- just draw the bitmaps on the squares. If the bitmaps are properly drawn, they
- should look just like buttons. For making the buttons depress, I would simply
- have additional bitmaps and draw them where appropriate.
-
- There are only a few downsides to this approach. First of all, bitmaps can not
- be stretched without introducing distortions. This means that whatever size I
- pick for them is going to be used throughout the game, regardless of user
- preferences. Second, bitmaps are difficult to re-color when loaded out of
- resource files, so the colors will not necessarily match the system-defined
- button colors. But these are minor concerns, I feel, given the difficulty of
- implementing the game in any other way.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3.2.1. The basic internal structure ΓòÉΓòÉΓòÉ
-
- Many variables are needed to keep track of the game. I chose to make most of
- the critical game variables global, so that they can be accessed from any
- procedure without large amounts of parameter passing. While this may be poor
- programming practice, it makes a game like this much easier to write and
- understand, in my opinion.
-
- In addition to the expected variables, like the size of the grid and the number
- of mines, three arrays are used. The arrays are dynamically allocated and
- re-allocated whenever the game grid changes size. All three arrays correspond
- to the game grid, one element corresponding to each square.
-
- One array is boolean, containing the map of mines - TRUE if the square has a
- mine, FALSE otherwise.
-
- The second array contains the visible screen. Each element contains a number,
- indicating what the user sees at that coordinate. This array is initialized to
- values corresponding to a blank raised button, but will change as play
- progresses. This is used primarily for displaying the screen.
-
- The third array contains the count of mines adjacent to each square. Rather
- than compute the number each time a square is uncovered, the entire set is
- computed in advance. This way, it's a quick operation to get the correct
- number when a square is uncovered.
-
- Other game variables include an array of bitmap handles for the drawing
- procedures, flags to an end-of-game condition, and the current timer count.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3.2.2. Using bitmaps as buttons ΓòÉΓòÉΓòÉ
-
- To implement the game grid, 18 bitmaps would be needed. I would need a blank
- square, squares with the numbers 1 through 8 on them, a "button" in the raised
- and depressed state, a "button" with a flag on it, a "button" with a question
- mark on it in the raised and depressed state, and bitmaps to indicate errors:
- an exploded mine, a misplaced flag, and an unflagged mine.
-
- All these bitmaps were drawn (on 16x16 16 color bitmaps) using the
- system-supplied icon editor. These were then all included in the resource file
- with lines like the following in mine.rc:
-
- BITMAP MINE_BLANK_UP MineBlankUp.BMP
- BITMAP MINE_BLANK_DOWN MineBlankDown.BMP
- BITMAP MINE_FLAG MineFlag.BMP
- BITMAP MINE_QUESTION_UP MineQuestionUp.BMP
-
- Where names like MINE_BLANK_UP are constants defined in the program's header
- file.
-
- Using bitmaps from a program's resources is fairly simple. First, a
- presentation space is required for drawing into. While a WinBeginPaint call
- will create a presentation space, I want to be able to draw bitmaps at times
- other than during WM_PAINT message processing. For this reason, I create a
- presentation space during processing of the WM_CREATE message and store its
- handle in a global variable. A presentation space may be created outside of
- paint message processing in the following way:
-
- hdc = WinOpenWindowDC(hwnd);
- sizl.cx = sizl.cy = 0;
- hps = GpiCreatePS(hab, hdc, &sizl, PU_PELS | GPIF_DEFAULT |
- GPIT_MICRO | GPIA_ASSOC );
-
- Where hwnd is the window handle passed as part of the WM_CREATE message, sizl
- is a variable of type SIZEL, and hab is the anchor block created as part of the
- program's WinInitialize function. The value of hps returned is used for all
- painting operations throughout the program. It is also passed to WinBeginPaint
- during WM_PAINT message processing to prevent spurious presentation spaces from
- being created.
-
- Once the presentation space exists, the bitmaps are all loaded from the
- program's resources with GpiLoadBitmap calls during the WM_CREATE message
- processing:
-
- hbitmap=GpiLoadBitmap(hps, NULLHANDLE, ID, BOX_WIDTH, BOX_HEIGHT);
-
- Where ID is the resource-ID for the bitmap, as defined in the resource file,
- and BOX_WIDTH and BOX_HEIGHT are constants indicating the size of the bitmap.
- If the height and width of the bitmaps do not match the constants provided,
- they will be stretched to fit the dimensions requested. The bitmap handles
- returned in hbitmap are all stored so that they do not need to be loaded again.
-
- Once handles are available for the bitmaps, displaying them is simple, using
- the WinDrawBitmap call:
-
- WinDrawbitmap (hps, hbitmap, NULL, &ptl, CLR_NEUTRAL, CLR_BACKGROUND,
- DBM_NORMAL);
-
- Where hbitmap is the handle of the bitmap requested, and ptl is a POINTL
- structure containing the window-relative coordinate of the lower-left corner of
- the bitmap's target position.
-
- The only other thing to consider is cleaning up when the program terminates.
- Although presentation spaces and handles will be released back to the system
- when the WinTerminate call is placed, it is good practice to release them
- manually, just to be sure nothing is left behind by accident. (Bugs in new
- systems, like OS/2 can cause this to happen, so it's better safe than sorry.)
- I place all cleanup instructions in the WM_DESTROY message handler. The
- following statements will delete bitmap handles and presenation spaces:
-
- GpiDeleteBitmap(hbitmap);
- GpiDestroyPS(hps);
-
- Where hbitmap is a valid bitmap handle and hps is the presentation space
- created earlier. The GpiDeleteBitmap function should be called repeatedly,
- once for each bitmap handle used in the program.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3.2.3. Using the mouse ΓòÉΓòÉΓòÉ
-
- Another significant problem of implementing the desired interface is that of
- getting a depressed "button" to track the mouse. For example, if the pointer
- is moved from one square to another while button 1 is pressed, the depressed
- button image on the screen should follow it.
-
- For reasons outlined previously, normal buttons won't provide this effect.
- Instead, using bitmaps, the entire effect must be simulated by handling
- appropriate messages. To properly implement button 1's behavior, we must
- handle the following messages: WM_BUTTON1DOWN, WM_BUTTON1UP, and WM_MOUSEMOVE.
-
- WM_BUTTON1DOWN is generated whenever mouse button 1 is pressed. This may be
- the left or right button, depending on whether OS/2 is configured for
- left-handed mouse operation or not. Since we want the left-right buttons to
- swap when in left-handed operating mode anyway, we can simply deal with button
- 1 and not concern ourselves with left or right.
-
- The WM_BUTTON1DOWN message provides the coordinates (window-relative) that the
- pointer is over when the button was pressed. These are delivered as the high
- and low words of the first message parameter and may be extracted with the
- following macros:
-
- x = SHORT1FROMMP(mp1);
- y = SHORT2FROMMP(mp1);
-
- Before taking action, we must be sure these coordinates are valid. Any time the
- button is pressed while the pointer is within the window's client area will
- cause a WM_BUTTON1DOWN message to be sent, and these coordiantes may not be
- over a segment of the game grid. ;p.If the coordinates are invalid, we simply
- break from the switch statement, and allow the WinDefWindowProc to handle the
- message.
-
- If the coordinate is valid, we then compute which grid square the coordinate is
- over. Since the grid is composed of same-sized rectangular regions, we have
- simply to subtract any margins and divide the coordinate by the width (or
- height) of a square to get the identity of the square.
-
- Once the square is identified, we re-draw it in its depressed form. In general,
- the depressed form of a square is identical to it's non-depressed form. The
- exceptions are the blank covered square and the question-mark covered square.
- These are depressable "buttons", and have different bitmaps for the depressed
- state and the raised state.
-
- A few other steps must also be taken, aside from drawing the depressed button.
- We must set a flag in a static variable indicating that the mouse button is
- down, so that the WM_MOUSEMOVE message handler will know the mouse button's
- position. We must also store the grid coordiante of the depressed button.
-
- Finally, we must capture the mouse. When the mouse is captured, the program
- will get all mouse messages, even if the pointer is over some other
- application's space. This is necessary, since the user may drag the pointer
- outside of our window and release the button there. If this happens, and the
- mouse isn't captured, the program will not receive the WM_BUTTON1UP message,
- and would think that the button is still down.
-
- The mouse is captured and released with the following functions:
-
- WinSetCapture (HWND_DESKTOP, hwnd); /* To capture the mouse */
- WinSetCapture (HWND_DESKTOP, NULLHANDLE); /* To release the capture */
-
- Where hwnd is the window handle provided to the message handler.
-
- The WM_BUTTON1UP message is returned when the user releases mouse button 1.
- Compared to the button down handler, this is simple. First, the capture on the
- mouse is released. Then, if the coordinate is over a covered-but-not-flagged
- square, we call the uncover function.
-
- If this is the first click of the game, then the timer is started.
-
- The WM_MOUSEMOVE message is sent to the application whenever the pointer
- position changes over the window. Normally, we will ignore these, passing them
- to the system's handler, WinDefWindowProc, but if button 1 is down, we want to
- take action first.
-
- First, we must check if the pointer's position has moved to another square. If
- it is over the same square as our saved position, then we do nothing. If it is
- not over the same square, then we have to draw the previous square in it's
- raised position, and draw the square it's now over in the depressed position
- and save the new coordinate.
-
- Managing the mouse button 2 is simpler, since flags and question marks are
- placed as soon as the button is depressed. When the WM_BUTTON2DOWN message is
- received and the pointer is over an appropriate square, the array element
- corresponding to the square is changed and the square is re-drawn.
-
- Managing the chord and button-3 sequences are similar to the button 1, except
- that nine boxes must now be drawn in the depressed state instead of one. This
- only gets a little ugly when the button 1 and button 2 messages must do
- double-duty to manage the chord sequence as well as their individual functions,
- but this is managed easilly with some well-placed if() statements.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3.2.4. Uncovering squares ΓòÉΓòÉΓòÉ
-
- Uncoverig a square seems like a simple procedure, but introduces interesting
- problems. The concept is simple. The program checks if the square is
- uncoverable and aborts if it is not. It then checks if a mine is being
- uncovered and ends the game if it is. Then it does the actual uncovering,
- ending the game if the player wins.
-
- In actuality, it's more complicated than this. As a courtesey, the program
- should never allow the user to uncover a mine on the first click, so if a mine
- is uncovered on the first click, the program should move that mine elsewhere.
- Also, if a square has no mines adjacent to it, the program should automatically
- uncover all the surrounding squares.
-
- Step one is simple. Check if the square is covered and does not have a flag on
- it. If not, just return and do nothing. There should also be some sanity
- checking here, in case the program tries (in error) to uncover a square that
- isn't on the grid.
-
- For step 2, the program must check if the square has a mine under it. If so,
- then the program must check if it is the first click. Moving the mine is tricky
- - the program must place a new mine, delete the old mine, and adjust the
- adjacency counters so all the numbers will still read proper values. Finally,
- it must then loop back to the start and retry uncovering the square.
-
- If it's not the first click, the finding a mine is game over. The timer is
- stopped and the endgame flag is set, which effectively prevents further play,
- since all of the mouse button message handlers check if the game is over as a
- part of their processing.
-
- If the square does not have a mine, however, it must be uncovered. In which
- case, the count of adjacent mines is fetched. If there are one or more
- adjacent mines, the bitmap for that number is assigned to that square, and it
- is displayed.
-
- If the uncovered square has zero adjacent mines, all the adjacent squares must
- be uncovered. At first, I simply had the uncover procedure call itself
- recursively, but I found that very large amounts of empty space would cause
- stack overflow problems, so I had to use an iterative solution, instead. I
- have a separate procedure to handle this, now.
-
- To iteratively calculate which squares to uncover, the program loops through
- all the squares. If it finds a covered square that is adjacent to an uncovered
- square with no adjacent mines, it sets the square to its uncovered state
- without calling the uncover procedure. It loops through the grid repeatedly
- until no changes are made to the grid.
-
- The program maintains a count of covered squares at all times. The count is
- initialized to the number of squares in the grid and is decremented whenever a
- square is uncovered. When the count of covered squares equals the number of
- mines, a win situation is declared.
-
- When the player wins, the timer is stopped, the endgame flag is set, and (if
- one of the three standard games is chosen) the score is compared to the high
- score, and if the high score is beaten, the score is recorded and the player
- may enter his name.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3.2.5. Saving and restoring settings ΓòÉΓòÉΓòÉ
-
- MineSweeper saves all critical game settings to an initialization file
- (MINE.INI) when it terminates and restores these settings from this file when
- restarting. The window's position, the high scores, and the game settings are
- among the items saved. This is all done using OS/2's profile management
- system.
-
- The profile management system is a set of PM calls (all beginning with Prf)
- that manage INI files. An INI file is opened by calling PrfOpenProfile, closed
- by calling PrfCloseProfile, and is read from and written to with other API
- calls.
-
- The calls I used, and the syntax for them are:
-
- hini = PrfOpenProfile (hab, "MINE.INI");
- PrfCloseProfile(hini);
- PrfWriteProfileData(hini, pszApp, pszKey, &data, sizeof(data));
- PrfWriteProfileString(hini, pszApp, pszKey, pszString);
- PrfQueryProfileData(hini, pszApp, pszKey, &buf, &buflen);
- PrfQueryProfileSize(hini, pszApp, pszKey, &buflen);
- PrfQueryProfileString(hini, pszApp, , pszKey, NULL, pszString, buflen);
-
- PrfOpenProfile takes two arguments. The first is the anchor block that is
- created when WinInitialize is called. The second is the file name of the INI
- file. A variable of type HINI is returned. This HINI variable is used to
- reference the INI file for reading and writing.
-
- Two preset HINI handles are also available: HINI_USERPROFILE and
- HINI_SYSTEMPROFILE may be used to access the user and system INI files, which
- are normally OS2.INI and OS2SYS.INI. I chose not to use these files, however,
- since software is required to delete entries from INI files. By keeping data
- in a separate initialization file, a user may erase all settings by simply
- deleting the MINE.INI file.
-
- PrfCloseProfile takes one argument: the HINI variable returned by the
- PrfOpenProfile call. It closes the profile. The user and system INI files are
- not closeable.
-
- Data in an ini file is referenced by two keys: and application name and a key
- name. These two keys, together, are used to reference arbitrary-sized blocks
- of data in the INI file. These blocks may be either strings or binary data.
-
- Almost all functions that read or write an INI file take the same first three
- paramters. The first being a valid HINI ini handle, the second being a string
- containing the application name, and the third being the key name.
-
- PrfWriteProfileData is used to write binary data to an INI file. It takes five
- parameters. The first three are the standard handle, app name and key name.
- The fourth is a pointer to the data, and the fifth is the length of the data.
-
- PrfWriteProfileString is used to write null-terminated string data to an INI
- file. It takes four paramters. The first three are the standard three, and
- the fourth is the string.
-
- PrfQueryProfileData is used to read binary data from an INI file. It takes
- five paramters. The first three are the standard three. The fourth parameter
- is a pointer to a buffer area to hold the data, and the fifth is a pointer to a
- long variable containing the size of the buffer. When the call completes, this
- variable will contain the actual number of bytes transferred.
-
- PrfQueryProfileString is used to read string data from an INI file. It takes
- six paramters. The first three are the standar three. The fourth is a default
- string that will be supplied if the key can not be found in the INI file; I
- leave this parameter as a NULL, indicating that I don't want a default string.
- The fifth paramter is a pointer to the buffer that will contain the string, and
- the sixth paramter is the maximum string length. The call will return the
- actual number of bytes transfered into the buffer.
-
- Finally, since I am dynamically allocating storage for these strings, I must
- find out the length of these strings before I actually read them in, in order
- to allocate a large enough buffer first. This is done with the
- PrfQueryProfileSize API call.
-
- PrfQueryProfileSize takes four parameters. The first three are the usual
- three, and the fourth is a pointer to a ULONG variable. This variable will
- contain the number of bytes that the referenced data block contains.
-
-
- ΓòÉΓòÉΓòÉ 4.2.3.2.6. Sizing the window ΓòÉΓòÉΓòÉ
-
- In MineSweeper, the mines are always the same size. And I do not want any
- margins around the minefield. This means that the window must be re-sized to
- fit the minefield.
-
- This is a two step process. First, the proper size must be calculated, then
- the window must be re-sized to fit.
-
- Calculating the window size is trivial, but not immediately obvious.
- Obviously, the width must be greater than the width of one row of squares, and
- the height must be greater than the height of one columns of squares. But this
- is not enough. The size of the window includes ALL of the window, including
- borders, menus, and title bars. So, the border width must by qyeried from the
- system and added to the width estimate. And the menu bar height, the title bar
- height, and the border height must also be queried and added to the height
- estimates.
-
- These values can be extracted with the WinQuerySysValue function:
-
- menuHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYMENU);
- captionHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYTITLEBAR);
- borderHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER) * 2;
- borderWidth = WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER) * 2;
-
- WinQuerySysValue takes two parameters. The first is a valid desktop handle,
- which (under OS/2 version 2.0) is always HWND_DESKTOP, and the second is a
- constant indicating which value to extract. The following were used:
-
- SV_CYMENU The minimum height for a menu bar. If the menu font is
- too large, a menu bar will actually become larger than
- this height. Additionally, if a menu bar is too long
- for the window, and wraps onto two or more lines, this
- value will only be the height of one line. In other
- words, it's not really very accurate.
- SV_CYTITLEBAR The height of a titlebar
- SV_CYBORDER The height of a thin border. Multiply this by two to
- get both borders.
- SV_CXBORDER The width of a thin border. Multiply this by two to
- get both borders.
-
- There are many other system values, but these are the only ones I needed. The
- actual width of the window is the width of the window's contents plus the
- border width. Unfortunately, the ambiguities of the menu bar's height do not
- make it that simple to generate the window's height.
-
- While there may be better ways to calculate the menu bar's actual height, I
- chose a quick-and-dirty approach. I first make a best guess of the window's
- height, by adding the system height values to the height of the playfield
- (consisting of the game grid and the score region). I then set the window to
- that size with the WinSetWindowPos command. After that I use the
- WinQueryWindowRect command to get the actual size of the menu bar. This works,
- because everything in OS/2 is a window - I get the window handle for the menu,
- and then query the window's size - giving me the size of the menu. Using this
- size, I re-calculate the height of the window and re-size it if it has changed.
- This all happens quickly, and no painting occurs during processing, so the user
- does not see the window change sizes.
-
- The window's size and position is set with the following call:
-
- WinSetWindowPos(hwnd, HWND_TOP, x, y, cx, cy, SWP_SIZE | SWP_MOVE |
- SWP_ZORDER | SWP_SHOW | SWP_ACTIVATE);
-
- Where hwnd is the window handle. The second parameter is for placing the
- window in the stack of open windows; HWND_TOP is a constant that tells the
- system to put this window above all the others. x, and y are coordinates for
- the lower-left hand corner of the window. cx, and cy are the horizontal and
- vertical sizes of the window. The last parameter is a set of flags. The ones
- presented here tell OS/2 to re-size the window, re-position it, change it's
- "stack" position, make it visible, and give it focus, respectively.
-
- The menu bar's size is fetched with the following calls:
-
- hwndMenu = WinWindowFromID(hwndFrame, FID_MENU);
- WinQueryWindowRect(hwndMenu, &rcl);
-
- Where hwndMenu is the window handle of the menu bar, hwndFrame is the window
- handle of the frame window that owns the menu bar, and rcl is a RECLT structure
- that contains the dimensions of the window.
-
- WinWindowFromID is an API call that returns the window handle of a child
- window. In this case, I want to know the handle of a menu-bar window, but I
- only know the handle of the frame window. So I use WinWindowFromID to extract
- the handle. It takes two parameters. The first is the handle of the parent
- window, and the second is the ID of the child. Menu bars that are created as
- part of a standard window always have an ID of FID_MENU.
-
- WinQueryWindowRect fills in a RECTL structure with the extents of a window.
- Since the coordinates are window-relative, the bottom and left extents are
- always 0 and 0, leaving the other two extents containing the width and height
- of the window.
-
-
- ΓòÉΓòÉΓòÉ 4.3. The Unofficial Guide to the Palette Manager ΓòÉΓòÉΓòÉ
-
- The Unofficial Guide
- to the
- Palette Manager
-
-
- ΓòÉΓòÉΓòÉ 4.3.1. The Guide ΓòÉΓòÉΓòÉ
-
- IMPORTANT NOTE: If you aren't going to read this whole article, at the very
- least please read about the FATAL ET4000 bug (search for FATAL ET4000 BUG if
- you're reading this as ASCII).
-
- Credit where it's due: When Windows 3.0 first came out with its Palette Manager
- in 256-color mode, I fell in love. Finally, a GUI that understood palettes and
- "did the right thing" when two programs tried to use the palette
- simultaneously. I was elated to find out that when OS/2 2.0 came out, it would
- also have a Palette Manager. My elation turned to disgust when I discovered the
- following paragraph in the READ.ME file for the OS/2 Programmer's Toolkit:
- Palette Manager functions are available, but no devices currently allow the
- physical palette to be changed. This means applications attempting to put
- customized colors into the palette will get the nearest colors from the default
- palette.
-
- As far as I'm concerned, this is tantamount to saying, "You can do anything you
- want ... as long as you don't want to do anything!" Fortunately, when the
- Service Pack came out later in 1992, the 32-bit XGA, Trident, and ET4000
- drivers all included good Palette Manager support (though with a few bugs, one
- of them serious). Sadly, as of this writing, I believe that people with other
- 256-color displays (such as 8514/A's and clones) still don't have decent
- Palette Manager support.
-
- So what's Palette Manager, and why should you care? VGA-standard adapters can
- display any of 262,144 different colors. Unfortunately, you can't display them
- all at once; you can only see 256 at a time. Fortunately, you can pick any 256
- of these colors at once (unlike the early CGA, where you had a choice of two
- sets of four fixed colors). The 256 colors that your adapter is showing at any
- time make up your physical palette. Under Presentation Manager, your program
- creates a logical palette. For example, you may want eight shades of red. You
- ask Palette Manager for eight colors, specifying the RGB values that you want.
- Palette Manager then gives you eight spaces in the physical palette and sets
- them to the RGB values you specified. If it runs out of space in the physical
- palette, then it looks at the colors it couldn't get for you and returns the
- colors in the physical palette that come closest to matching them. In either
- case, you don't have to worry about which spots in the physical palette you
- got; you just draw lines in color 3 (for instance), confident that they will be
- medium red.
-
- (At this point, some of you might say, "Couldn't you do this with a Logical
- Color Table?" I think the answer is yes [I've never used LCTs myself], but I
- think Palette Manager is easier to use and more flexible. Also, I'm sort of
- getting the impression that LCTs are being phased out.)
-
- There are other things you can do with Palette Manager. Broadly speaking, there
- are four different types of tasks you can carry out using Palette Manager:
-
- o Examining (but not changing) the physical palette
-
- o Creating a logical palette for drawing (e.g., using GpiBox)
-
- o Creating a logical palette for displaying a bitmap with special color needs
-
- o Creating and animating a logical palette
-
- In an ideal world, the third task would just be a special case of the second.
- However, bugs in the Palette Manager require you to do a few special things to
- get it to work, so I'll treat it separately.
-
- My program PHYSCOLO demonstrates the first task, SCALE demonstrates the second,
- GRAYBITM demonstrates the third, and ZOOM demonstrates the fourth. Although
- these programs do very different things, they have many similarities. They (and
- any other program that uses Palette Manager) carry out the following steps:
-
- 1. Make sure Palette Manager is available.
- 2. Create a PS (and DC)
- 3. Create the logical palette
- 4. Select the palette into the PS
- 5. Realize the palette
- 6. Use the palette
- 7. Clean up by deselecting the palette, destroying it, and destroying the PS.
-
- Let's look at how we carry out these tasks. At this point, I strongly suggest
- that you follow along with a copy of PHYSCOLO.C. Either print it out or open it
- up in another window.
-
- STEP ONE: Make sure Palette Manager is available.
-
- In the "main" part of PHYSCOLO, you'll find the following block:
-
- {
- LONG sColors;
- HDC hdcScr;
- hdcScr = GpiQueryDevice (WinGetPS (HWND_DESKTOP));
- DevQueryCaps (hdcScr, CAPS_ADDITIONAL_GRAPHICS, 1, &hasPalMan);
- hasPalMan &= CAPS_PALETTE_MANAGER;
- ...
- }
-
- (There are two more lines in the actual listing that check how many colors are
- available on your computer. While all existing drivers with Palette Manager
- support have exactly 256 colors, this could change in the future.)
-
- After running this block, hasPalMan will be zero if the computer doesn't
- support Palette Manager. PHYSCOLO just keeps going, but it might be more
- appropriate to give an error message and exit. (See ZOOM, for instance.)
-
- So if hasPalMan is nonzero, are you okay? Not necessarily. Remember the OS/2
- Toolkit READ.ME: OS/2 may report that you have Palette Manager support even if
- the device won't allow your physical palette to change. Unfortunately, I don't
- know any way to tell if you "really" have Palette Manager support; if anyone
- knows, I'd like to hear from them.
-
- STEP TWO: Create a PS (and DC)
-
- Here's the First Commandment of using Palette Manager:
-
- IF YOU'RE GOING TO USE PALETTES, !!DON'T!! USE A CACHED MICRO-PS!
-
- If someone had told me this, it would have saved me a couple of weeks. If you
- only learn two things from this article, learn this and the Fatal ET4000 bug
- (below).
-
- In other words, don't do the following when handling WM_PAINT:
-
- case WM_PAINT:
- {
- HPS hps;
- hps = WinBeginPaint (...);
- ...
- }
-
- Why? When you create a cached micro-PS, it "forgets" everything between
- WM_PAINT messages. While that's fine for simple graphics, it's not appropriate
- when you're using a palette, because the cached micro-PS will forget the
- palette!
-
- Instead, we create an uncached micro-PS. Looking at the "ClientWinProc"
- procedure in PHYSCOLO, we see the following lines:
-
- static HPS hps;
- static HDC hdc;
-
- (These are static, of course, since they have to be remembered between calls to
- ClientWinProc.) Later on, we see the rest of the lines for creating the PS:
-
- case WM_CREATE:
- {
- SIZEL sizl;
- sizl.cx = sizl.cy = 0;
- hdc = WinOpenWindowDC (hwnd);
- hps = GpiCreatePS (hab, hdc, &sizl, PU_PELS | GPIF_DEFAULT
- | GPIT_MICRO | GPIA_ASSOC);
- }
-
- STEP THREE: Create the Palette
-
- This step (and Step Five) will differ depending on which task you carry out
- (e.g., whether the logical palette is for examining the physical palette or for
- palette animation). In the case of PHYSCOLO, we do the following:
-
- static HPAL hpal;
- ...
- WM_CREATE:
- {
- ...
- ULONG tbl [MAX_PAL_SIZE];
- INT j;
- for (j = 0; j < colorsToShow; j++) {
- tbl [j] = PC_EXPLICIT * 16777216 + j;
- }
- hpal = GpiCreatePalette (hab, 0L, LCOLF_CONSECRGB,
- colorsToShow, tbl);
- }
-
- Four things worth pointing out:
-
- 1. Ordinarily, it's a good idea to sort the palette, with the most important
- RGB values closer to the beginning of tbl. Here, since PHYSCOLO merely
- examines the physical palette, we don't sort entries.
- 2. The PC_EXPLICIT flag in each tbl entry means, "This number represents an
- entry in the physical palette, and NOT a color." So, for instance,
- PC_EXPLICIT * 16777216 + 3 means the third entry in the physical palette.
- 3. The LCOLF_CONSECRGB flag means that tbl is an array of RGB values, each of
- which is 4 bytes long (which is why we can "fake" it with ULONGs).
- Currently, you must use this flag for all calls to GpiCreatePalette (and
- your table must be an array of RGBs / ULONGs).
- 4. The 0L parameter is appropriate for PHYSCOLO, since all we're doing is
- examining the physical palette. Later, I'll try to convince you that it's
- appropriate for every other use of GpiCreatePalette, too.
-
- STEP FOUR: Select the Palette into the PS.
-
- This is easy:
-
- GpiSelectPalette (hps, hpal)
-
- STEP FIVE: Realize the Palette.
-
- You realize the palette when your program changes its logical palette or in
- response to OS/2's new WM_REALIZEPALETTE message. From what I've seen,
- WM_REALIZEPALETTE is sent to a Presentation Manager program when that program
- moves into the foreground, or when some other program changes an (unreserved)
- palette entry. (Reserved palette entries are ones used for palette animation;
- see below.)
-
- If your program doesn't start in the foreground, it should realize the logical
- palette before it first draws using that palette. Otherwise, the client area
- will be drawn in black. That's what the
-
- STATIC BOOL firstPaint = TRUE;
- ...
- if (hasPalMan && firstPaint {
- ...
- WinRealizePalette (hwnd, hps, &palSize);
- }
-
- stuff in the WM_PAINT case is for.
-
- The parameters to WinRealizePalette are straightforward, though I wish I
- understood why it's necessary to pass a pointer to the palette size rather than
- the palette size itself.
-
- The other time PHYSCOLO calls WinRealizePalette is when handling
- WM_REALIZEPALETTE. When PHYSCOLO receives a WM_REALIZEPALETTE message, it
- merely calls WinRealizePalette and ignores the return value. This is a little
- atypical, as I'll explain below.
-
- STEP SIX: Use the palette.
-
- This is easy. Looking at PHYSCOLO's listing, we see the line
-
- GpiSetColor (hps, j);
-
- What this means is, use the color of the j-th logical palette entry for your
- next drawing operation. That's all there is to it.
-
- STEP SEVEN: Clean up by deselecting the palette, destroying it, and destroying
- the PS.
-
- When you're completely finished with your palette, you should deselect it and
- destroy it. When you're completely finished with your PS, you should destroy
- it. Usually, this will be when your program is ending, which is why all these
- tasks are handled in the WM_DESTROY code for PHYSCOLO:
-
- if (hasPalMan) {
- GpiSelectPalette (hps, NULLHANDLE);
- GpiDestroyPalette (hpal);
- }
- GpiDestroyPS (hps);
-
- This should be completely straightforward. Be sure you get rid of every palette
- and PS when you're done with it.
-
- OTHER USES OF PALETTE MANAGER
-
- We've gone through PHYSCOLO and seen how to examine the physical palette. What
- changes do we have to make to create a custom logical palette for drawing, or
- for displaying a bitmap, or for animating a palette? The answer is, not many.
- Most of the changes are in Steps Three and Five.
-
- SCALE
-
- This program draws a grayscale, or a redscale, or a cyanscale, or .... See
- SCALE.DOC for details.
-
- If we consider how it uses the Palette Manager, SCALE isn't really all that
- different from PHYSCOLO. It does Steps One and Two (checking for the Palette
- Manager and creating the PS and DC) the same way that PHYSCOLO does. When we
- reach Step Three, things are slightly different. In SCALE, the entries in tbl
- represent RGB triples, and they take the form:
-
- tbl [j] = red * 65536 + green * 256 + blue * 1;
-
- where "red," "green," and "blue" are the RGB values for the color you desire in
- logical palette entry j. These values range from 0 (meaning none of that
- primary) to 255 (meaning the maximum amount of that primary). Here's an
- example: Bright cyan is formed by mixing the maximum amount of green with the
- maximum amount of blue. Numerically, this would be 255 * 256 + 255 * 1, or
- 65535. So, for example, if tbl[3] had a value of 65535, this would mean that
- the third entry in the logical palette would be bright cyan. (We don't use the
- PC_EXPLICIT flag since we want to create colors, rather than examine physical
- palette entries.)
-
- Note that I still use 0L in the call to GpiCreatePalette. Why? Well, there are
- two possible alternative options (which can even be OR'd together):
-
- LCOL_PURECOLOR. According to the online docs (in the OS/2 Toolkit), "If this
- option is set, only pure colors are used and no dithering is done." Well, I've
- never used this option and I've never seen any dithering. In my honest opinion,
- Palette Manager does the closest match instead of dithering, since dithering
- wouldn't work for narrow lines (or single pixels!), and since it'd be too
- computationally expensive for filled areas. Anyway, you can try this flag if
- you want; I don't use it and don't plan to.
- LCOL_OVERRIDE_DEFAULT_COLORS. OS/2 Presentation Manager sets aside about 20
- colors for the user interface (eg, for the color of Window title bars). This
- means that you can't ever really get 256 colors. Unless you use this flag, that
- is. However, using this flag will screw up the look of the user interface. In
- my honest opinion, it's not worth it; sort your palette and live with the fact
- that you'll "only" get 236 distinct colors.
-
- Step Four is the same old "GpiSelectPalette (hps, hpal)."
-
- Step Five is half the same (in handling firstPaint), but half different. In the
- handling of WM_REALIZEPALETTE, we actually use the return value of
- WinRealizePalette:
-
- ULONG palSize = colorsToShow;
- if (WinRealizePalette (hwnd, hps, &palSize)) {
- WinInvalidateRect (hwnd, NULL, FALSE);
- }
-
- What does this do? Basically, when you call WinRealizePalette, it returns the
- number of palette entry requests that it could NOT meet. If you asked for a
- palette with 16 colors and it could only give you 13, the other 3 will be
- matched to the closest available colors, and WinRealizePalette will return 3.
- In this case, you should redraw your client area, and that's what the
- "WinInvalidateRect" call does.
-
- Step Six, using the palette, is much the same as it was for PHYSCOLO. Notice,
- however, that SCALE also has a routine to handle things if the machine doesn't
- even pay lip service to supporting Palette Manager. That's what the
- "GpiQueryColorIndex" line is about. On a system without Palette Manager, this
- returns the argument to GpiSetColor that will return the closest match to the
- RGB value you want.
-
- Finally, Step Seven is the same for SCALE as it was for PHYSCOLO.
-
- GRAYBITM
-
- This program creates a small bitmap with grayscale shapes and displays it on
- the screen (stretching it to fit the client area). It's pretty much the same as
- SCALE. There are a couple of differences: It creates the palette before
- creating a PS, and it uses the logical palette in two different PSes; the one
- that's used to create the bitmap (hpsMem in the "CreateBitmap" procedure), and
- the one that's used to display the bitmap (hps in "ClientWinProc"). These
- differences aren't really that important, though the latter shows that you can
- "share" palettes by sharing the HPAL.
-
- Notice that handling WM_PAINT is simplicity itself: We just call WinDrawBitmap.
- But why didn't we use GpiBitBlt? And why do the colors get screwed up when we
- resize (eg, maximize) the GRAYBITM window? We'll tackle these issues when we
- talk about the bugs in the Palette Manager.
-
- ZOOM
-
- This program does palette animation. What's that? Well, suppose you do the
- following:
-
- o Create a logical palette with space for four colors, with all four palette
- entries set to the window background color.
- o Draw four non-overlapping filled circles using each of the four colors, with
- the circle that uses color 0 on the left, circle 1 to the right of it, and so
- on.
-
- Okay, so at this point you've drawn four "invisible" circles. Now suppose you
- set the first entry in the logical palette to blue (or any color that isn't the
- background). Boom, a circle appears on the left. After a brief pause, change
- the first color in the logical palette back to the background, and change the
- second color to blue. Then change the second color to the background and the
- third color to blue. And so on. If you time these well, this makes it look like
- an animated sequence of a circle moving from left to right across your client
- area. But the thing worth pointing out is, you don't have to do any fancy
- drawing or undrawing in real-time; all you're doing is changing palette
- entries. Since you're only changing palette entries, this sort of animation can
- go very fast, once you've finished the complicated initial drawing.
-
- ZOOM does something similar: It draws 16 overlapping squares, creates a palette
- with spaces for 16 colors, and then animates. The result is a little like
- moving down a long corridor. (For a real cheap thrill, run TESTZOOM.CMD,
- CTRL-ESC to get the Window List, select all the Zoom sessions, and Tile them.
- If one of these windows has the focus, click on the desktop to "get rid" of
- it.)
-
- From a programmer's standpoint, ZOOM is yet another straightforward use of
- Palette Manager. It carries out Steps One and Two in the usual way (though it
- gives an error message and dies instead of just "trudging on" if it can't find
- Palette Manager -- since this program exists only to demonstrate palette
- animation, there's no point in having it continue.)
-
- In Step Three, things are slightly different:
-
- tbl [j] = PC_RESERVED * 16777216 + ...
-
- What's this PC_RESERVED flag? Well, as I said before, if Palette Manager can't
- give you all the colors you requested, it will use the closest match among the
- physical palette. (That's why we do a WinInvalidateRect if WinRealizePalette
- returns a nonzero value.) However, if you use the PC_RESERVED flag, Palette
- Manager will not match anything with that particular palette entry. This is
- important if the RGB values of the palette entry will be changing rapidly (the
- overhead of keeping the closest match up-to-date would be too great). Since
- changing RGB values rapidly is the heart of palette animation, you should
- always use the PC_RESERVED flag for palette animation.
-
- Step Four is still the same. Steps Five and Six are different. In fact, I'd say
- that Steps Five and Six are now intertwined, since the way you "use" a palette
- for animation is to constantly change it, and you have to realize the palette
- every time you change it. Hence:
-
- ULONG tmp = tbl [0];
- ...
- tbl [j] = tbl [j + 1];
- tbl [shadesToShow] = tmp;
- GpiAnimatePalette (hpal, LCOL_CONSECRGB, 0, shadesToShow, tbl);
-
- GpiAnimatePalette is, naturally enough, the function that actually animates
- (ie, changes the RGB values of) the logical palette. Its arguments are the
- handle to the palette to change, the ubiquitous LCOL_CONSECRGB, the first index
- to change, the number of indexes to change, and the table that holds the new
- RGB values. The call above changes all the palette entries; to change only
- entries 3 through 7, change the "0" above to "3", and the "shadesToShow" to
- "5". (The values in tbl would still be the same.)
-
- Finally, Step Seven is the same as usual.
-
- PALETTE MANAGER BUGS
-
- There are several reasons why this guide is unofficial rather than official.
- One reason is that it describes what I've discovered in the school of hard
- knocks, which is sometimes quite different from what the official documents
- say. Another reason is that I don't have any access to official IBM folks, so
- there may well be errors in this article. I've described how I've used Palette
- Manager to the best of my ability, but it could be that there are simpler, more
- elegant ways to carry out some of these tasks. I doubt it, but we'll never know
- until the IBM documentation improves.
-
- (Another reason that this is unofficial is that I have no compunction against
- slamming IBM when I think they deserve it.)
-
- Finally, one of the important reasons why this is an unofficial guide is that
- I'm going to tell you that the current incarnations of Palette Manager have
- some real bugs, including one that's very serious. Namely:
-
- THE FATAL ET4000 BUG (or, How To Hang Your Computer Using Palette Manager)
-
- This is the most important fact in this article; if you forget everything else,
- remember this. And warn anyone who uses your program about it!
-
- If you're running one of the 256-color ET4000 SuperVGA drivers from the Service
- Pack, AND IF a program changes the physical palette (either an OS/2 program
- that uses Palette Manager or a seamless WinOS/2 program), THEN !!DON'T MOVE AN
- ICON ON THE OS/2 DESKTOP!! IF YOU DO, YOUR COMPUTER WILL HANG!
-
- Your computer won't be safe until the physical palette has reverted to normal.
- This won't happen until after the program(s) that changed the palette are
- finished. And even then, it can take a while; one way to force the palette back
- is to start PHYSCOLO (or move it to the foreground) and then click on the
- desktop. The palette will revert to normal.
-
- TRIDENT USERS BEWARE! THIS BUG MAY ALSO OCCUR FOR YOU (I couldn't get
- verification by press time).
-
- Fortunately, this bug does NOT occur in the XGA drivers. Neither does it occur
- in the latest 256-color drivers from the OS/2 2.1 beta (version 6.479), so
- ET4000 users may want to switch to the beta. I hope the fact that the bug isn't
- in the beta means that IBM has fixed it, but I haven't heard anything official
- from them, so take care.
-
- Unfortunately, this isn't the only bug in Palette Manager. I know of three
- others:
-
- THE UNIVERSAL BUG: Start a program that uses Palette Manager to draw in its
- window (eg, SCALE or GRAYBITM). Drop down the system menu over the client area.
- Click elsewhere to get rid of the menu. The area under the menu won't redraw
- correctly. This bug occurs on every platform that's known to support Palette
- Manager, though it's less obvious on XGAs and 2.1 beta-using ET4000s than on
- Tridents and ET4000s with the Service Pack drivers.
-
- THE GPIBITBLT BUG: If you use GpiBitBlt to render a bitmap that uses a logical
- palette, it'll be drawn with the wrong colors on ET4000s and Tridents.
- Guaranteed. However, it'll be drawn fine on XGAs.
-
- THE WINDRAWBITMAP SCALING BUG: If you use WinDrawBitmap with parameters that
- require the bitmap to stretch or shrink, it'll be drawn with the wrong colors
- on ET4000s and Tridents. Again, though, it'll look fine on XGAs.
-
- I have sent bug reports from IBM on all of these, as well as sample programs
- that demonstrate the GpiBitBlt bug. However, I'm just one voice crying in the
- wilderness: If you want to see these bugs fixed, PLEASE get the file
- OS2PROB.TXT from the /pub/os2/2.0/info directory on ftp-os2.nmsu.edu, fill it
- out, and e-mail it to 76711.610@compuserve.com. Make sure that you only report
- ONE bug on each form; otherwise, you'll just get a note back from IBM asking
- you to resubmit your report on several forms. As long as I am the only person
- asking for these bugs to be fixed, they'll probably be a low priority for IBM.
-
- FINAL THOUGHTS
-
- Don't let these bugs get you down; Palette Manager is a fine subsystem that
- lets you accomplish a lot of great effects. My hat's off to IBM for giving us
- such a simple and elegant way to use palettes, and my hat's off to YOU for the
- fine Palette-Manager-using programs that I hope you'll be writing soon ;-).
-
- Raja Thiagarajan / sthiagar@bronze.ucs.indiana.edu / 2-21-93
-
-
- ΓòÉΓòÉΓòÉ 4.4. Advanced GPI:Retained Segments and Transformations ΓòÉΓòÉΓòÉ
-
- Advanced GPI:
- Retained Segments
- and
- Transformations
-
-
- ΓòÉΓòÉΓòÉ 4.4.1. Advanced GPI ΓòÉΓòÉΓòÉ
-
- [The code presented in this article is contained in GPI.ZIP -Ed.]
-
- When you say "Graphic Program Interface" what first comes to mind is a set of
- about a dozen and a half system calls to draw lines, boxes, circles, and other
- kinds of graphic elements. But, I may ask you, did you know that by using OS/2
- Presentation Manager, you could refresh your entire window, whatever you draw,
- with one call? Did you know that you can take graphic "segments" and do things
- like rotate them, translate them, and scale them, without actually having to
- write the code to do the matrix math yourself? Well, thats what we're going to
- be talking about. There is a whole host of functions that are built into the
- GPI of OS/2's presentation manager that perform these features, plus more.
- Unfortunately, these functions are not normally used by the common user, and
- thats why I'm going to be talking about them here. This is an introduction to
- the Advanced GPI functions that many of us don't usually think about.
-
- Just as an introduction, I'm assuming that you've programmed OS/2 presentation
- manager programs before. Therefore, I'm not going to go into the specifics of
- the calls such as WinCreateStdWindow, or any of the other Window functions,
- since this article mainly deals with Gpi functions. But, if you've never
- programmed PM before, I do think that you should still be able to follow this
- article through to the end, and by looking at the source code, which I hope is
- clear and straightforward, you should be able to pick up with what I'm talking
- about. So, I would also recommend that you have a copy of the source code
- around while you're reading this, since I'll be talking about distinct parts of
- the code, and to get a better view of whats going on, you should see the
- context of how the call is used.
-
- First, we're going to talk about presentation spaces. What exactly is a
- presentation space and how can we use it to help us? Very generally, a
- presentation space is the part of the system that takes your calls to the GPI
- functions, and generates the pictures that you see on the screen. In most
- cases, this mapping of graphic calls to displayed results is normally a
- pass-through operation, that is, the things that you do aren't retained in
- memory, other than the results that you see on the screen. This is the default
- behavior of the presentation space that is associated with the client window
- that would be created by a WinCreateStdWindow call, this type of presentation
- space is called the "Cached Micro-PS." But, there are other types of
- presentation spaces, the most important of these being the "Normal PS." What a
- Normal PS allows you to do is take the graphic calls that you're sending it,
- and remember them for later operations, like refreshing the window, or rotating
- your objects, or whatever you like. A Normal PS also supports correlation
- functions that will let you know if any of your graphic segments pass through a
- certain rectangle of the screen, that you define. This correlation is
- wonderful for user interface tasks, such as clicking on an object to select it.
- We'll see how to use these functions later in the article.
-
- The program that is developed here displays nine objects on the screen, each of
- which is its own segment in the presentation space. By clicking on an object,
- with either the right or left button, you can select an object. When an object
- is selected, you can use the '+' and '-' keys to rotate it, or drag with the
- right mouse button to translate it. The '=' key does the same thing as the '+'
- key, for ease of use.
-
- Since we know that the default Presentation Space create with the
- WinCreateStdWindow call isn't a Normal PS, we have to figure out how to create
- the PS that we really want. Looking through our list of the GPI functions, we
- might take notice of GpiCreatePS.
-
- HPS GpiCreatePS(HAB hab,HDC hdc,PSIZEL pszl,LONG loptions)
-
- "hab" is the Handle to our Anchor Block that we got when we did our first
- WinInitialize call. This should be readily available as a global variable to
- our program. HDC is the Handle of the Device Context that is associated with a
- window, or in layman's terms, a handle that describes what type of device we're
- working with. This is readily available by calling the WinOpenWindowDC
- function. So, all we need now are the pszl that defines the size of the
- presentation space that we want to create, and some options. Looking at the
- functional description of the call, we see that if we specify (0,0) as the size
- of the PS we're creating, a default size for our device will be used instead.
- So, we're left with some options, that describe what type of PS we're going to
- be creating. In our case, we want the Page Units to be pixels (since we're
- drawing on the screen), we want a Normal PS, and we want to asociate the PS
- with the hdc that we're dealing with, so we tell it to automatically do this
- for us. So, our final version of the call ends up being:
-
- sizel.cx=0;
- sizel.cy=0;
- hps=GpiCreatePS(hab, hdc, &sizel, PU_PELS | GPIF_DEFAULT
- | GPIT_NORMAL | GPIT_ASSOC);
-
- And, we can use this HPS throughout our program, as long as we follow a couple
- simple conventions, the main one being that we destroy it at the end of our
- program. Normal PS's take up a lot of memory, and you don't want to be using
- them where you don't have to. Use a normal PS only when you want to use the
- functions that go along with it. You'll quickly run out of memory if you use a
- normal PS for every window that you create.
-
- Looking at our WM_CREATE message in our client window procedure, we see the
- above call to GpiCreatePS, along with a couple other suspicious looking calls,
- the first of these being the GpiSetDrawingMode command. Before explaining the
- call, I'll explain a little more about the process that will be going on in the
- program.
-
- We know that we have now a Normal PS instead of a Cached Micro PS and we know
- that we can have retained graphics using this normal PS. Well, it would seem
- fairly obvious that there has to be some way of more readily organizing the
- information (graphics) that we're going to be retaining. What the Gpi can do
- for us is break our individual calls to Gpi primitives into larger blocks, each
- of which is a segment. Each segment has a "tag" that goes along with it. This
- "tag" is just a number that we assign to the segment so that we can remember
- which one is which. The segments also have numbers, and we'll order them
- sequentially. In our program, the segment number and the tag are the same
- number, for simplicity's sake. Note that to do the functions that we're doing,
- each segment must have its own unique non-zero tag. Along with the tag, a
- segment transform matrix is also stored. If you're not familiar with computer
- graphics and transform matrices, just think of this as something that will
- specify the position and rotation angle of the segment. It can also specify
- the scaling value of a segment, but we won't deal with those calls in this
- example program. The calls to the scaling routines are very similar to the
- ones for translation and rotation, so if you have a reference manual that
- describes these functions, you should be able to use them with no problem. So,
- we've got our segments. For this program, we're going to have nine segments.
- Two will be five pointed stars, one a square with rounded corners, one an oval,
- and the rest will be squares. They'll be arranged in a 3x3 grid on the screen.
- We also must mote that matrices in OS/2 are mainly composed of fixed point
- numbers instead of floating point. This allows the Gpi to not require a
- floting point coprocessor and still run at a resonable speed on slow computers.
- Thus, there is a macro called MAKEFIXED that takes two arguments, an integer
- portion and a fractional portion of the number. As a little background, to
- represent a number as a fixed point, call our number 'n'. You would use the
- following code:
-
- i=(INT)n;
- f=(n*65536)-((INT)n*65536);
- fixed=MAKEFIXED(i,f);
-
- So, back to talking about the GpiSetDrawingMode command. What this does is
- tell our presentaion space to do one of the following things: Draw only, draw
- and retain, or retain only. We're going to be using the retain only function,
- so that we can enter the segments into the presentation space, then draw them
- ourselves with other specialized calls later. So, we use the call
-
- GpiSetDrawingMode(hps,DM_RETAIN);
-
- To set the drawing mode. The other suspicious looking call is the
- GpiSetPickApertureSize call. I'll come back to this later. What it does is
- set the size of the rectangle that we'll be using as our correlation area.
-
- Now, the last thing that's there is the SetupSegments call. This is a
- procedure that was written to draw the nine segments, and set their initial
- segment transform matrices so that they're not rotated, and so that they're
- arranged in a 3x3 grid. Looking at this procedure, we see some calls that look
- somewhat unfamilar. GpiSetInitialSegmentAttrs. Oh, this must have to do
- something with the segment attributes. Yes, it does. Every segment has some
- flags that go along with it, this just tells the system "for every new segment
- that you make, set these options from now on" The options that are most
- commonly used are:
-
- 1. ATTR_VISIBLE The segment will be visible -- i.e. will be drawn
-
- 2. ATTR_DETECTABLE The segment will be detectable by the correlation
- functions
-
- 3. ATTR_CHAINED The segment will be in the segment chain.
- The segment chain is the list of segments that will be drawn by the
- GpiDrawChain call. So, in our case, we want all of our segments to be in the
- segment chain. Now, the second unusual looking call is the GpiScale call.
- What we're doing here is initializing the variable 'identity' so that it
- contains the identity matrix, so that we can set our viewing transformation to
- be the identity. What this means is that if we say our square is 30x30, it
- will come out as 30 pixels on a side, exactly. So, we call
- GpiSetViewingTransformMatrix with the appropriate arguments. The one that
- might look suspicious is the second one, the 9L. This specifies that there are
- nine elements in our matrix. This is because for two dimensional
- transformations, you deal with 3x3 matrices. The rest of the code is organized
- as follows:
-
- GpiOpenSegment();
- GpiSetTag();
- GpiSetColor();
- GpiTranslate();
- GpiSetSegmentTransformMatrix();
- .
- .
- .
- Draw our segment
- .
- .
- .
- GpiSetModelTransformMatrix();
- GpiCloseSegment();
-
- What we're doing is keeping a counter of the segments that we've drawn, so that
- each segment will have a unique number. We open that segment, set its tag, and
- its color. The GpiTranslate call sets the matrix 'translate' to be matrix that
- will translate the image drawn from (0,0) to the point specified in
- ptlTranslate. Then, using the GpiSetSegmentTransformMatrix call, we set the
- matrix for the current segment, to be the translation matrix that we just
- computed. Along with the individual segment transform matrices for each
- segment, there is a model transform matrix which is applied to all segments
- drawn. When a segment is drawn, its transform matrix gets multiplied to the
- model transform matrix, resulting in an accumulation of the transforms. To get
- around this effect, at the end of each segment we set the model transform to be
- the identity. We then close the segment we're working with.
-
- So, after the SetupSegments call, we've got nine retained segments, each with
- its own individual transform matrix to put it in the right spot on the screen.
- The hard part is through. We've already drawn the innards of our segments, an
- operation that only has to happen once, and we've also set their initial
- values, something that only has to happen once. From here on out, its just the
- accumulation of the deltas that make up the other operations we can do on the
- segments.
-
- Looking at the WM_PAINT messaage, which is the next things that happens, we see
- that it is very short and concise. We erase the presentation space, set the
- forground mix to XOR mode, so that we can easily erase a segment once we've
- drawn it, then we call the infamous GpiDrawChain, which draws all the chained
- segments, along with applying their individual transform matrices. Note that
- the WinBeginPaint call in our WM_PAINT message is probably a little different
- than those that you've seen before. Because WinBeginPaint usually returned a
- hps, but we have a PS already, we have to tell it that, so the second argument
- to the call is our current hps. This way it doesn't get obliterated by the new
- PS that would have been created.
-
- The next message that I'll look at is the WM_BUTTON?DOWN messages, they're both
- the same, so I'll just talk about WM_BUTTON1DOWN. What we're going to be doing
- here is correlating the retained segments to see if we've clicked on one or
- not. To do this we have to do a couple of setup things first. Intuitively, we
- have to set the size and position of the rectangle that we're going to be
- correlating with, then do the actual correlation. Because the size of the
- correlation rectangle doesn't change throughout the program, I've placed it in
- the WM_CREATE message, so it only gets executed once. Take a look at the code,
- it sets the variable 'size' so that its 10 pixels square, then calls
- GpiSetPickApertureSize. This calls are fairly obvious, first the hps, then the
- options, then the pointer to the variable 'size'. The option PICKAP_REC tells
- the Gpi system that we want to use the value specified in the third argument,
- as opposed to using some default vaule. Now for the position of the pick
- aperture. Back to the WM_BUTTON1DOWN message, we see that the variable 'ptl'
- is set to be the current position of the mouse, and we call
- GpiSetPickAperturePosition. Its arguments are self explanatory. Next comes
- the neat call, the correlation.
-
- lNumHits=GpiCorrelateChain( HPS hps,
- LONG lType,
- POINTL pptlPick,
- LONG lMaxHits,
- LONG lMaxDepth,
- PLONG alSegTag);
-
- lType specifies what type of segments you want to correlate with. In our case,
- we want to do all segments, so we specify PICKSEL_ALL. The pptlPick argument
- is again the position of the pick aperture. lMaxHits and lMaxDepth together
- specify the maximum number of hits that will be recorded by this function. In
- my case, as in most cases, both of these arguments will have the value one,
- since we only are interested in one segment at a time. The PLONG alSegTag is
- an output array of segment number and tag value pairs. For example, on return,
- if we've hit a segment alSegTag[0] will be the segment number of the selected
- segment, and alSegTag[1] will be its tag vaule. The routine returns the number
- of segments hit, so in our case, this will be either zero or one, and we're
- only interested in the case where its nonzero. If the call returns nonzero, we
- set our variable 'selected' to be the index of the currently selected segment,
- and continue, otherwise we set 'selected' to be zero, to signify that nothing
- is currently selected.
-
- Now, when we get a WM_MOUSEMOVE message, and when button two is down, we do
- some more magic. What we want to accomplish here is to figure out how far the
- mouse was moved at each step, and then add that increment to the current
- position of the currently selected segment. The first thing that we do is draw
- the segment in XOR mode, which will erase it from the screen. We compute the
- position delta, and then use GpiTranslate to get the matrix that represents
- that translation. Using GpiSetSegmnetTransformMatrix with the TRANSFORM_ADD
- parameter, we then add this delta translation to the total translation. The
- TRANSFORM_ADD parameter tells the Gpi to postmultiply the matrix onto the
- current matrix. Because we want all rotations to come before all translations,
- we postmultiplt translations, and premultiply rotations. The last thing that we
- do here is draw the segment again, so that it appears in its new place. The
- GpiDrawSegment call draws just one segment, which is specified as the second
- argument to the call.
-
- The last part of our program that we havn't looked at yet is the WM_CHAR
- message. It too is fairly simple. It sets the rotation angle to be 3.0
- degrees, then if the '-' key has been pressed, negates this value. It then
- premultiplies the right matrix onto the segment transform matrix with the
- GpiRotate and GpiSetSegmentTransformMatrix calls. Note that the parameter to
- GpiSetSegmentTransformMatrix is TRANSFORM_PREEMPT.
-
- I've described all the Gpi functions that allow you to create segments, and
- modify the segment transform matrices so that you can rotate and translate
- them. Hopefully I've given enough background so that you can write your own
- programs that use these advanced Gpi functions, along with the others that deal
- with retained segments and segment transform matrices. In most cases, these
- calls aren't as hard as they seem, and it is guaranteed that using these calls
- will both save you programming time, and increase the speed of your own code.
- Have fun!
-
-
- ΓòÉΓòÉΓòÉ 5. Columns ΓòÉΓòÉΓòÉ
-
- o Questions and Answers
-
- o Introduction to PM
-
- o Project Barrel
-
-
- ΓòÉΓòÉΓòÉ 5.1. Questions and Answers ΓòÉΓòÉΓòÉ
-
- Questions
- and
- Answers
-
-
- ΓòÉΓòÉΓòÉ 5.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- Welcome to the first "Questions and Answers"! Each month, I collect various
- questions sent to me via email and try to answer each directly; the ones that I
- feel contribute the most to developers, whether in terms of information or as a
- nifty trick to tuck into your cap, get published in this column.
-
- To submit a question, send mail to my email address - os2man@panix.com - and be
- sure to grant permission to publish your question (those that forget will not
- be considered for publication).
-
-
- ΓòÉΓòÉΓòÉ 5.1.1.1. "Smart icons" ΓòÉΓòÉΓòÉ
-
- Stefan Gruendal (Stefan_Gruendel@wue.maus.de) writes:
-
- But to my question: How do I build my own "smart icons", i.e. bitmaps on
- pushbuttons, that optically "move into the screen"? I didn't find any way to
- achieve this with the Toolkit's Dialog Editor. But as mentioned above, I know
- there's a way.
-
- Starting with OS/2 2.0, a new button style was added - BS_ICON - which allows
- you to do what you are trying to accomplish. It works, as far as I know, only
- for pushbuttons, and the icon or bitmap is taken from the resource file, whose
- resource id is specified in the pushbutton text.
-
- For example:
-
- In FOO.H:
-
- #define IDBM_BUTTON 256
- #define IDPB_BUTTON 257
-
- In FOO.RC:
-
- BITMAP IDBM_BUTTON BUTTON.BMP
-
- In FOO.C:
-
- sprintf(achText,"#%d",IDBM_BUTTON);
-
- hwndButton=WinCreateWindow(hwndClient,
- WC_BUTTON,
- achText,
- WS_VISIBLE|BS_PUSHBUTTON|BS_ICON,
- 10,
- 10,
- 32,
- 32,
- hwndClient,
- HWND_TOP,
- IDPB_BUTTON,
- NULL,
- NULL);
-
- The bitmap is stretched or compressed to fill the button.
-
-
- ΓòÉΓòÉΓòÉ 5.1.1.2. Finding yourself ΓòÉΓòÉΓòÉ
-
- Raja Thiagarajan (sthiagar@bronze.ucs.indiana.edu) writes:
-
- Can a PM program tell if there's a previous instance of itself running? In
- Win3.x (but apparently NOT Win32), there's a hPrevInst handle; is there an OS/2
- 2.0 equivalent? Basically, I'm thinking in terms of a program that would try
- to borrow resources from a previous instance if a previous instance is running.
- (Specifically, if my palette animation program gets started twice, the second
- instance oughta share palettes with the first instance!)
-
- What you're really asking is two questions:
-
- 1. How can I determine if a previous instance of my application is already
- running?
- 2. How can I share resources between multiple instances of my application?
-
- To answer your first question, you need to enumerate all of the main windows
- present on the desktop, and figure out if any of them are yours. This is
- achieved using the following code:
-
- HWND queryAppInstance(PCHAR pchClassWanted)
- {
- HENUM heEnum;
- HWND hwndTop;
- HWND hwndClient;
- CHAR achClass[256];
-
- heEnum=WinBeginEnumWindows(HWND_DESKTOP);
-
- hwndTop=WinGetNextWindow(heEnum);
- while (hwndTop!=NULLHANDLE) {
- hwndClient=WinWindowFromID(hwndTop,FID_CLIENT);
- if (hwndClient!=NULLHANDLE) {
- WinQueryClassName(hwndClient,sizeof(achClass),achClass);
- if (strcmp(achClass,pchClassWanted)==0) {
- WinEndEnumWindows(heEnum);
- return hwndClient;
- } /* endif */
- } /* endif */
-
- hwndTop=WinGetNextWindow(heEnum);
- } /* endwhile */
-
- WinEndEnumWindows(heEnum);
- return NULLHANDLE;
- }
-
- To answer your second question, the only way that I know of to share resources
- is to place them in a DLL. The procedure to do this is as follows:
-
- o Create a dummy source file with an empty procedure in it.
- o Compile the source file and link as a DLL.
- o Add your resources to the DLL in the same manner as you would to an
- executable.
-
- Then, when your application starts, you simply call WinLoadLibrary (the
- preferred way) or DosLoadModule to load the DLL. These functions return a
- handle to the DLL which must then be used in any function which loads resources
- (e.g. GpiLoadBitmap, WinLoadPointer, etc.).
-
- Note that this procedure does not require knowing the window handle of any
- previous instance of your application because OS/2 implements DLLs in a shared
- fashion (which I suspect is one of the reasons they were created in the first
- place). All you need to know is the name of the DLL. This technique can also
- be used to share resources between different applications.
-
-
- ΓòÉΓòÉΓòÉ 5.1.1.3. Device independence ΓòÉΓòÉΓòÉ
-
- A reader who desires to remain anonymous writes:
-
- Generally: My understanding was that OS/2 would handle printing for me. That
- is to say that I wouldn't have to create separate printer drivers for every
- printer under the sun (or any for that matter). Since I am creating an image
- on the screen that is device independent (well, mostly anyway), is there an
- easy way to get printer output?
-
- PM achieves a level of device independence by defining a logical output space.
- This logical output space is then bound to a physical output space, which
- creates a mapping of logical characteristics to their physical counterparts.
- The logical and physical output spaces are referred to as the presentation
- space and the device context (HPS and HDC) and are bound to one another by
- using either the GpiAssociate function or by specifying GPIA_ASSOC to the
- GpiCreatePS function.
-
- The easiest way to accomplish what you desire is to organize your drawing code
- into one or more functions with a single entrypoint that accepts an HPS as a
- parameter. Then, when you want to draw to the screen, you can call
- WinGetPS/WinBeginPaint to get an HPS and call the function. When you want
- hardcopy, you call DevOpenDC to get an HDC and GpiCreatePS to get an HPS and
- call the function.
-
- Note that to get hardcopy, you need to perform some additional setup to get
- things to work properly. The two most important things are that you initialize
- the DEVOPENSTRUC structure properly before calling DevOpenDC and that you send
- the following escape codes (via DevEscape) at the following times:
-
- hdcPrn=DevOpenDC(...);
- hpsPrn=GpiCreatePS(...);
-
- DevEscape(...,DEVESC_STARTDOC,...);
-
- if (!doDraw(hpsPrn)) {
- DevEscape(...,DEVESC_ABORTDOC,...);
- } /* endif */
-
- DevEscape(...,DEVESC_ENDDOC,...);
-
- GpiDestroyPS(hpsPrn);
- DevCloseDC(hdcPrn);
-
- I'm not sure because I can't seem to find my copy anywhere, but I belive that
- the book by Graham Winn (entitled something to the effect of "Building
- applications using the OS/2 Presentation Manager") dedicates a chapter to the
- nuances of printing.
-
-
- ΓòÉΓòÉΓòÉ 5.2. Introduction to PM ΓòÉΓòÉΓòÉ
-
- Introduction to
- PM Programming
-
- Part I
-
-
- ΓòÉΓòÉΓòÉ 5.2.1. Overview ΓòÉΓòÉΓòÉ
-
- Overview
-
- It can be quite a daunting task to sit down and learn to write Presentation
- Manager programs. There is so much ground to cover, but fear not - getting over
- the initial learning curve is the hardest part. Once you have a grasp of the
- basics, it is fairly easy to pick up on more advanced techniques.
-
- Here I intend to provide a starting point for C programmers to get into PM, so
- that you do have a solid grounding. Now unfortunately there are not many books
- on PM which explain not just the what, but the why. That is, not just
- presenting a syntax diagram and saying that (for example) WinBeginPaint is used
- when you want to paint a window, but why you need it, when to use it (and not
- WinGetPS ), and how to use it.
-
- I will endeavour to explain the why, so you understand exactly what you are
- doing, instead of blindly copying code from examples. You will see the code,
- and at each step we will discuss the important sections, so you can see just
- what each API does. You certainly don't have to remember every single API
- though (there are hundreds). As you have a need to do something, you will learn
- the new APIs required to achieve that.
-
- Please remember that, as this is an introductory article, it is not presented
- as being 100% technically correct and accurate. It is more to give the reader a
- practical feel for what is going on. Technical references for technical
- questions appear in the Bibliography. And remember, you can always read the
- headers!
-
-
- ΓòÉΓòÉΓòÉ 5.2.2. Scope ΓòÉΓòÉΓòÉ
-
- Scope
-
- I am assuming that you are a competent C programmer, and have a working
- knowledge of OS/2 from a user's perspective. The sample code here was produced
- with IBM's C Set/2, and is ANSI C. I do not assume anything but a familiarity
- with GUIs in general.
-
-
- ΓòÉΓòÉΓòÉ 5.2.3. Trademarks etc. ΓòÉΓòÉΓòÉ
-
- Trademarks etc.
-
- Please note that any trademarks referred to in this article remain the property
- of their respective companies.
-
- #include <std_disclaimer.h>
-
-
- ΓòÉΓòÉΓòÉ 5.2.4. Presentation Manager ΓòÉΓòÉΓòÉ
-
- Presentation Manager
-
- Presentation Manager is the GUI which sits atop OS/2. It consists mainly of a
- series of DLLs which contain all the controls, APIs and other stuff which
- makes PM tick. The WPS is in effect a PM application itself, albeit with
- certain other special properties. One good thing about PM is that because the
- bulk of the code to handle the user interface is in DLLs, applications tend to
- be smaller, as they contain largely just the code to handle the processing of
- the information. This lets the programmer focus on the "guts" of the program,
- rather than being concerned with the user interface. Also, PM provides a
- consistent and intuitive interface for the user along standard design
- guidelines. Of course, as you will no doubt see, there is still a great deal to
- learn about PM!
-
-
- ΓòÉΓòÉΓòÉ 5.2.4.1. The Event Driven Model ΓòÉΓòÉΓòÉ
-
- The Event Driven Model
-
- GUIs are modelled on an Event Driven paradigm. This is better understood when
- contrasted with the sequential nature of conventional programs. Imagine the
- following ficticious code which might appear in the menu of a program:
-
- DisplayMenu();
-
- while (!kbhit())
- UpdateTime();
-
- c=getch();
-
- if (c==0x27)
- Quit();
-
- You can see that in the while loop, the program is constantly polling the
- keyboard to check if a key has been pressed. This is a very inefficient
- practice, as the machine spends most of its time in that loop, doing nothing.
- In a multitasking environment, this waste of CPU time reduces system
- throughput, as the CPU could be doing something more useful.
-
- In OS/2 however, programs do not poll for input (whether from the mouse or
- keyboard). All input is managed by PM itself. This means that when the user
- clicks the mouse on a button for example, PM handles this event. It puts the
- event into a queue for that application, and the function which handles this
- event (the window procedure) only gets called when it has a message waiting for
- it. As a result, the program only acts when it needs to, rather than constantly
- checking if it actually has something to do.
-
- So rather than following sequentially through the program, PM programs send,
- receive and respond to events. This technique requires a shift in thinking, but
- with practice it will become clear. It is arguably a more intuitive model for
- programming, and it also promotes good technique.
-
-
- ΓòÉΓòÉΓòÉ 5.2.4.1.1. Windows Everywhere ΓòÉΓòÉΓòÉ
-
- Windows Everywhere
-
- A Window is simply a rectangular area on the screen. A window can have certain
- properties or flags, and PM is full of them. They may or may not have a frame
- or border, a titlebar, or a menu.
-
- You may only think of a window as being the Frame window of an application.
- However buttons are windows too. So are dialog boxes, list boxes, scrollbars -
- in fact each control in PM is a window. We will explore this further in a
- future article, but for now just take it that a window is rectangle.
-
- Don't worry too much if this sounds a bit vague - it will become clear once you
- start making your own windows.
-
-
- ΓòÉΓòÉΓòÉ 5.2.4.1.2. Getting the Message ΓòÉΓòÉΓòÉ
-
- Getting the Message
-
- The messages discussed in our talk of Events is simply a C struct, which looks
- like this:
-
- typedef struct _QMSG /* Queue Message */
- {
- HWND hwnd;
- ULONG msg;
- MPARAM mp1;
- MPARAM mp2;
- ULONG time;
- POINTL ptl;
- ULONG reserved;
- } QMSG;
-
- This struct is a key element in PM. The fields are explained thus:
-
- Γûá hwnd - Window Handle - unique reference for a given window.
-
- Γûá msg - A system- or user-defined message
-
- Γûá mp1 and mp2 - Message parameters (meaning dependent upon the message). Can
- be a number, a pointer, a handle, etc.
-
- Γûá time - The time stamp when the event ocurred.
-
- Γûá ptl - The (x,y) co-ordinates of the mouse.
-
- Γûá reserved - Just what it says!
-
- All messages have a unique number, and the system messages are all #defined in
- the OS/2 headers. Some typical messages include:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéMessage ΓöéDescription Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéWM_CHAR ΓöéWhen the user presses a key Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéWM_CLOSE Γöéif the user chooses to Exit the program Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéWM_PAINT Γöéwhen an area of the screen needs to be Γöé
- Γöé Γöédrawn Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- You will not normally deal with the QMSG structure directly - your window
- procedure will handle this, and deals only with the handle, the message and the
- two parameters (mp1 and mp2).
-
-
- ΓòÉΓòÉΓòÉ 5.2.4.1.3. Having a Handle on Things ΓòÉΓòÉΓòÉ
-
- Having a Handle on Things
-
- A handle is a unique number or reference for any given system object. The most
- common handles refer to windows. An application can have any number of windows,
- and handles are a convenient way to keep track of them. Handles are also used
- internally by PM to manage its own information on the system.
-
-
- ΓòÉΓòÉΓòÉ 5.2.4.1.4. Window Procedures ΓòÉΓòÉΓòÉ
-
- Window Procedures
-
- A Window Procedure is one of the most important parts of any PM application.
- Its job is to handle all of the messages that get sent to its window. It
- usually consists of a big switch statement, with a case for each message. You
- can handle as many or as few messages as you wish, because (fortunately!) one
- of the APIs in PM is a default window procedure. So any message you don't need
- or want to handle, you pass on to WinDefWindowProc, which handles it in the
- default manner.
-
- Well, enough of this banter - let's do something useful and make a PM program.
-
-
- ΓòÉΓòÉΓòÉ 5.2.5. Your first PM Program ΓòÉΓòÉΓòÉ
-
- Your first PM Program
-
- Let's start simply by getting a plain window on screen. I will show you the
- code, followed by explanations, with the full source at the end of this
- section.
-
- /* Definitions */
-
- #define INCL_WINFRAMEMGR
-
- #define MYAPP "MyApp"
- #define ID_MYWINDOW 42
-
- If you look at the file OS2.H, you will see a whole bunch of #ifdefs, and
- #includes. Because the header files are so large and numerous, and you don't
- always want to include everything since you may not use all of it, all you need
- do is #define which parts you need to include. There are certain things which
- are included by default, so here we only neet to define INCL_WINFRAMEMGR, for
- the definition of WM_ERASEBACKGROUND, as all the rest is already there. The
- most important header is PMWIN.H, so take some time to browse through it as it
- has some very important things in it.
-
- /* Includes */
- #include <os2.h>
-
- Having defined which sections we need from the headers, all we need at the
- moment is to include OS2.H.
-
- /* Prototypes */
-
- MRESULT EXPENTRY MyWinProc (HWND, ULONG, MPARAM, MPARAM);
-
- We prototype our functions, like all good ANSI programmers!
-
- /* Global Variables */
-
- HAB hab;
- HWND hwndClient,
- hwndFrame;
-
- The HAB stands for a Handle to the Anchor Block. As you know, a handle is just
- a reference, but the anchor block is an internal data structure which PM
- manages. The origin of the name is a little obscure. Anyway, each instance of
- an application has an HAB. There are few times when you actually use it,
- usually only during the initialization of your program. For now, let's say it
- is like a handle to your application.
-
- Now we have two other handles, this time handles to windows. Notice that we
- have two handles. One for the client, one for the frame. This requires a little
- more detail.
-
- The Frame Window is the window from the edges of the resizeable frame border.
- It contains the border, titlebar, minimize and maximize buttons, system menu,
- and optionally a menu.
-
- The Client Window is the area of whitespace bordered by the frame and the menu.
- This is the space where information is displayed, and the user interacts with.
- Take a look at the System Editor for example. The frame window encompasses the
- entire application window. The client area is the area of white where the text
- appears (the edit window). Having a separate client window makes it easier to
- paint, scroll, and other such operations.
-
- /* main function */
-
- void main (void)
- {
- HMQ hmq;
- QMSG qmsg;
- ULONG flCreate;
-
- hab = WinInitialize (0);
- if (!hab)
- return;
-
- Here we launch straight into the main function. First we declare some local
- variables. The HMQ is a handle to a message queue. When an application is
- created, it needs a queue to keep the pending messages in, as they await
- processing by the window procedure.
-
- The QMSG is the data structure we looked at earlier (see Messages). This is
- used in the main message loop below. The flCreate is used to keep the flags we
- need to create our window.
-
- The very first thing we do is call WinInitialize. This initializes certain
- things in PM and gives us an HAB. If we could not get a HAB, something really
- bad has gone wrong, so we exit immediately.
-
- hmq = WinCreateMsgQueue (hab, 0);
-
- Here we create our message queue with our HAB, and get a handle to it so we can
- refer to it later.
-
- WinRegisterClass(
- hab,
- MYAPP,
- (PFNWP) MyWinProc,
- CS_SIZEREDRAW,
- 0);
-
- Because each application behaves differently and has their own window
- procedure, PM needs to know certain things about your application. This is
- called registering. You give it your HAB, a unique string (called the class
- name) which identifies your program, a pointer to your window procedure (which
- is why you cast it) and any particular flags you may require. These flags are
- called Class Styles, and CS_SIZEREDRAW means we want our application to be
- redrawn if it is resized. The last parameter allows us to reserve some space
- within each window for instance-specific data, but we don't need this yet.
-
-
- ΓòÉΓòÉΓòÉ 5.2.5.1. Creating the Window ΓòÉΓòÉΓòÉ
-
- Creating the Window
-
- flCreate = FCF_TITLEBAR | FCF_SYSMENU |
- FCF_SIZEBORDER | FCF_MINMAX |
- FCF_TASKLIST | FCF_SHELLPOSITION ;
-
- hwndFrame = WinCreateStdWindow(
- HWND_DESKTOP,
- WS_VISIBLE,
- &flCreate,
- MYAPP,
- "Sample PM App",
- 0L,
- NULLHANDLE,
- ID_MYWINDOW,
- &hwndClient);
-
- First we set up a variable which has ORed together a bunch of flags (called
- Frame Control Flags) to tell PM what sort of window we want. They mean (in
- order) we want a titlebar, a system menu, a resizeable border, both minimize
- and maximize buttons, we want it to appear in the task list, and we don't care
- what it's initial size or position is (let the shell decide).
-
- Now comes the actual window creation. We specify the parent window (usually the
- desktop, as is here). WS_VISIBLE means we want it to be visible, pass the frame
- control flags, our class name (so it knows which window procedure to use), the
- title of the window, then any other special flags. NULLHANDLE tells it to look
- for Resources (such as icons, menus, dialogs, etc) in the executable itself. We
- pass the ID of the window (to distinguish it from other windows, and to
- associate resources with), and finally get a handle to our client window. I
- hope you're keeping up so far, because we're almost there!
-
- while (WinGetMsg (hab,&qmsg,(HWND)NULL,0,0))
- WinDispatchMsg (hab,&qmsg);
-
- This is the most crucial part of the program, and is really what gives it the
- capability to respond to events. It is a constant loop which gets a message
- from the queue and dispatches it to the window procedure. WinGetMsg will keep
- going until it receives the WM_QUIT message. WinDispatchMsg actually gets PM to
- call your window procedure, which is why all window procedures have the same
- parameters (hwnd, msg, mp1, mp2). The other parameters in WinGetMsg allow you
- to do fancy things which we won't get into now.
-
- WinDestroyWindow(hwndFrame);
- WinDestroyMsgQueue(hmq);
- WinTerminate(hab);
- }
-
- This is just for cleanup - once the user exits the application, we clean up and
- release the resources we used.
-
- /* our window procedure */
-
- MRESULT EXPENTRY MyWinProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
- {
-
- As I mentioned above, because PM calls your window procedure, you must have it
- declared exactly like this. EXPENTRY tells the compiler that this function will
- be in the list of exports (a list of " public" functions).
-
- switch (msg)
- {
- case WM_ERASEBACKGROUND:
- return (MRESULT) TRUE;
- break;
-
- Here we begin our amazing switch statement, which branches on the message. For
- each case, we have a Window Message. This first one is PM asking if we want it
- to erase the background for us. If we return true, it will do so. If we want to
- paint the background (or client area) ourselves, we would return False.
-
- case WM_CLOSE:
- if (WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,
- "Are you sure you want to quit?","Sample",
- 0,MB_YESNO | MB_QUERY) == MBID_YES)
- WinPostMsg(hwnd,WM_QUIT,0L,0L);
- break;
-
- This case is somewhat special. WM_CLOSE is sent when the user presses Alt-F4,
- selects Close from the System menu, double clicks on the System menu, selects
- Close from the Window List, or selects File|Exit from the menu (if there is
- one). This gives us a chance to ask the user if they are sure they want to
- quit, and make sure there are no changes to any files they might wish to save.
- Here we use the WinMessageBox API, passing it the parent and owner, the message
- we wish to display, the title of the message box, an ID (used for providing
- Help), and some flags. The flags specify that we want Yes and No buttons, and a
- Query icon. The call will return MBID_YES if they pressed the Yes button, in
- which case we send our application the WM_QUIT message. This causes WinGetMsg
- (in main) to return FALSE, the while loop to terminate, and finally cleanup
- before exiting.
-
- default:
- return WinDefWindowProc (hwnd, msg, mp1, mp2);
- }
- return FALSE;
- }
-
- This part makes life easy for us. There are a myriad of other messages that our
- application receives, and WinDefWindowProc will handle them all for us. This is
- part of the beauty of the event-driven model - we grab only the messages which
- interest us, and let PM do the rest. This gives us a standard look and feel,
- with no extra work.
-
- Now we have written a bare-bones PM application, follow the instructions in the
- next section to run it.
-
-
- ΓòÉΓòÉΓòÉ 5.2.5.2. Instructions ΓòÉΓòÉΓòÉ
-
- Instructions
-
- You can extract the source code to a file. To do this, for each file, go to the
- code, press Ctrl-F. This will create a file called TEXT.TMP in the root
- directory of the current drive. Go to an OS/2 Window and copy that file to a
- working directory, renaming it to the respective name (which appears in the
- title). You should be able to compile and run the program straight away. Just
- issue the command:
-
- [C:\WORK\PM]nmake /f step01.mak
-
- And then run:
-
- [C:\WORK\PM]step01
-
- You should see a plain window with a titlebar. Closing the window will confirm
- with a dialog. Once you have seen the program running, go back and re-read the
- section explaining the code, so you can see just what is happening.
-
-
- ΓòÉΓòÉΓòÉ 5.2.6. Sample Code ΓòÉΓòÉΓòÉ
-
- Sample Code
-
- [The code presented in this article is also available in 'intropm.zip'. -Ed.]
-
- Γûá STEP01.C
-
- Γûá STEP01.MAK
-
-
- ΓòÉΓòÉΓòÉ 5.2.6.1. STEP01.C ΓòÉΓòÉΓòÉ
-
- /* Definitions */
-
- #define INCL_WINFRAMEMGR
-
- #define MYAPP "MyApp"
- #define ID_MYWINDOW 42
-
- /* Includes */
-
- #include <os2.h>
-
- /* Prototypes */
-
- MRESULT EXPENTRY MyWinProc (HWND, ULONG, MPARAM, MPARAM);
-
- /* Global Variables */
-
- HAB hab;
- HWND hwndClient,
- hwndFrame;
-
- /* main function */
-
- void main (void)
- {
- HMQ hmq;
- QMSG qmsg;
- ULONG flCreate;
-
- hab = WinInitialize (0);
- if (!hab)
- return;
-
- hmq = WinCreateMsgQueue (hab, 0);
- WinRegisterClass(
- hab,
- MYAPP,
- (PFNWP) MyWinProc,
- CS_SIZEREDRAW,
- 0);
-
- flCreate = FCF_TITLEBAR | FCF_SYSMENU |
- FCF_SIZEBORDER | FCF_MINMAX |
- FCF_TASKLIST | FCF_SHELLPOSITION ;
-
- hwndFrame = WinCreateStdWindow(
- HWND_DESKTOP,
- WS_VISIBLE,
- &flCreate,
- MYAPP,
- "Sample PM App",
- 0L,
- NULLHANDLE,
- ID_MYWINDOW,
- &hwndClient);
-
- while (WinGetMsg (hab,&qmsg,(HWND)NULL,0,0))
- WinDispatchMsg (hab,&qmsg);
-
- WinDestroyWindow(hwndFrame);
- WinDestroyMsgQueue(hmq);
- WinTerminate(hab);
- }
-
- /* our window procedure */
-
- MRESULT EXPENTRY MyWinProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
- {
- switch (msg)
- {
- case WM_ERASEBACKGROUND:
- return (MRESULT) TRUE;
- break;
-
- case WM_CLOSE:
- if (WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,
- "Are you sure you want to quit?","Sample",
- 0,MB_YESNO | MB_QUERY) == MBID_YES)
- WinPostMsg(hwnd,WM_QUIT,0L,0L);
- break;
-
- default:
- return WinDefWindowProc (hwnd, msg, mp1, mp2);
- }
- return FALSE;
- }
-
-
- ΓòÉΓòÉΓòÉ 5.2.6.2. STEP01.MAK ΓòÉΓòÉΓòÉ
-
- #--------------------------------------------------------------------
- #
- # Makefile for STEP01.MAK
- #
- #--------------------------------------------------------------------
-
- CC = icc
- LINK = link386
- CFLAGS = /c /Ti
- LFLAGS = /pmtype:pm /debug
-
- ┬╖c.obj:
- $(CC) $(CFLAGS) $*.c
-
- all: step01.exe
-
- step01.exe: step01.obj
- $(LINK) $(LFLAGS) step01,,,os2386,;
-
-
- ΓòÉΓòÉΓòÉ 5.2.7. What Next? ΓòÉΓòÉΓòÉ
-
- What Next?
-
- Now that you have a basic PM application running, in the next issue we will add
- a menu, some dialogs, and respond to some messages that do something useful. It
- may seem like a lot of code to just get a plain window to appear, but we will
- see how easy it is to add features next month.
-
- I welcome any feedback on this article (netmail preferred) - any comments or
- suggestions you may have, questions on this article, or things you would like
- to see in a future article. I hope you have learned something!
-
-
- ΓòÉΓòÉΓòÉ 5.2.8. Bibliography ΓòÉΓòÉΓòÉ
-
- Bibliography
-
- The following references were used in the preparation of this article:
-
- Γûá OS/2 Version 2.0 - The Redbooks
-
- Γûá OS/2 Version 2.0 Technical Library
-
-
- ΓòÉΓòÉΓòÉ 5.3. Project Barrel ΓòÉΓòÉΓòÉ
-
- Project Barrel
-
-
- ΓòÉΓòÉΓòÉ 5.3.1. Project Ideas ΓòÉΓòÉΓòÉ
-
- This column is a place where I intend to throw out ideas that I have come
- across, either by thinking of them myself or by listening around on the net.
- Some of these ideas are more practical than others; all of them should be worth
- at least thinking about.
-
- Feel free to attack anything you see here; similarly, feel free to send in your
- ideas, in as much or as little detail as you wish.
-
- Icon Management - A commercial application is available that does most of this
- now. However, Windows users typically don't have to pay for this level of
- utility and there's no reason we should either. Ideally, the program would
- allows for easy movement into and out of zipfiles (or a similar compressed
- storage archive) and allow for drag 'n drop attachment to programs. The ability
- to view more than the 100 or so the drives object can view would also be a
- plus.
-
- Network software - Let's face it, the software that comes with TCP/IP 1.2.1 is
- crap. Telnet only runs in 25 line mode, LaMail is a hideous ordeal to set up,
- RN doesn't even work. There's a lot of opportunity here to write one of those
- apps that people use every day of their lives.
-
- Games - OS/2 Klondike is nice, but how about something that really shows off
- what a 32 bit multithreaded program can do? For Presentation Manager, StarTrek
- springs to mind as a possibility, as does an RPG a'la Castle of the Winds. In
- fullscreen mode anything is possible. I would also like to see some multiplayer
- network games - I just saw a friend playing Bolo on the Mac and didn't see any
- reason why I shouldn't have something like it on my machine.
-
- Emacs - Yes, I actually like the beast (I did this entire magazine with Emacs).
- What I would like (and I have a vested interest in this) is an emacs major mode
- for editing .IPF files. One that would match tags (in the same way that c-mode
- matches parentheses) and provide for a consistent style with headers, comments,
- etc.
-
- Etc. - Ever look and see how HUGE the Windows areas are on most BBS's? There's
- no reason why there shouldn't be that much stuff out there for OS/2 - the two
- systems are similar in programming style and there's even an entire C compiler
- for OS/2 free for the asking. Anytime you see a Windows program you like,
- figure out what it is you like and port it!
-
-
- ΓòÉΓòÉΓòÉ 6. Future Attractions ΓòÉΓòÉΓòÉ
-
- Coming up in the future, we have:
-
- o Introduction to PM Part 2
-
- o Writing Installable File Systems
-
- o Getting Started with IPF
-
- o And much more!
-
-
- ΓòÉΓòÉΓòÉ 7. Contributors to this issue ΓòÉΓòÉΓòÉ
-
- o Steve Luzynski
-
- o David Charlap
-
- o Larry Salomon
-
- o Raja Thiagarajan
-
- o Gavin R Baker
-
- o Steve Lacy
-
-
- ΓòÉΓòÉΓòÉ 7.1. Steve Luzynski ΓòÉΓòÉΓòÉ
-
- Steve Luzynski is the editor and creator of this magazine. He is currently a
- Computer Engineering student at Case Western Reserve University in Cleveland,
- OH, where he spends a lot of time being cold. Steve has yet to release any
- programs for OS/2 as a direct result of: 1) editing this magazine; and 2)
- having to waste time going to class when there are programs to write. Steve can
- by reached via e-mail at 'sal8@po.cwru.edu' or on Compuserve at 72677,2140.
-
-
- ΓòÉΓòÉΓòÉ 7.2. David Charlap ΓòÉΓòÉΓòÉ
-
- David Charlap is 23 years old and is a full-time student at New Jersey
- Institute Of Technology. He is pursuing a Masters degree in computer science.
- He learned OS/2 programming while working for a software development company
- and is currently writing small OS/2 applications for shareware consumption.
- MineSweeper is his first application that has been released to the general
- public. David may be reached by e-mail at:
-
- dic5340@hertz.njit.edu
-
- He may also be reached at the following address:
-
- David Charlap
- 8 Snow Ridge
- Denville, NJ 07834
- USA
-
-
- ΓòÉΓòÉΓòÉ 7.3. Larry Salomon ΓòÉΓòÉΓòÉ
-
- Larry Salomon wrote his first Presentation Manager application for OS/2 version
- 1.1 in 1989. Since that time, he has written numerous VIO and PM applications,
- including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen
- Capture trio included with the IBM Professional Developers Kit CD-ROM currently
- being distributed by IBM. Currently, he works for International Masters
- Publishers in Stamford, Connecticut and resides in Bellerose, New York with his
- wife Lisa.
-
-
- ΓòÉΓòÉΓòÉ 7.4. Raja Thiagarajan ΓòÉΓòÉΓòÉ
-
- Raja Thiagarajan was born in Madras, India in 1965, but moved to Bloomington,
- Indiana by the age of two in order to get a 25-year headstart on writing "An
- Unofficial Guide to the Palette Manager." Raja has college degrees in
- Astrophysics and Computer Science, but will never understand NASA's priorities,
- the C preprocessor, Scheme's call/cc, or Prolog's cut operator. Raja would love
- to hear from you; send e-mail to sthiagar@bronze.ucs.indiana.edu
-
-
- ΓòÉΓòÉΓòÉ 7.5. Gavin R Baker ΓòÉΓòÉΓòÉ
-
- Gavin R Baker
-
- Gavin R Baker is the man behind ThinkSoft, a consulting firm based in
- Melbourne Australia which specialises in developing custom software. He has
- experience in Assembler, Pascal, C, C++, (and a lot of other languages), and
- has worked with Unix, DOS, Windows, OS/2, VMS and Pick operating systems. He is
- an active member of Team OS/2. When he isn't programming, he is also a
- musician, an actor, and wastes lots of time reading Net News. He can be
- contacted thusly:
-
- net: demogrb@lust.latrobe.edu.au
- bix: gbaker
- cis: 100026,270
-
-
- ΓòÉΓòÉΓòÉ 7.6. Steve Lacy ΓòÉΓòÉΓòÉ
-
- Steve Lacy is a third year computer science student at Carnegie Mellon
- University, and is currently overoccupied with school, and his job as an
- undergraduate research programmer for the Digital Mapping Lab at CMU. You can
- reach Steve via e-mail at sl31@andrew.cmu.edu. He would be glad to hear any and
- all of your comments about the previous article. Steve also spends his free
- time collecting CD's, and unfortunately spends far too much money in the
- process. Remember, diversity is knowledge, and personal gratification
- overrides the need for food in many cases.....
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Graphical User Interface
-
- An interface usually comprising multiple overlapping windows, icons, pull-down
- menus, and the mouse as a pointing device. It is recognised that the GUI was
- invented by Xerox at PARC (Paolo Alto Research Centre) in the late 60's (??).
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Dynamic Link Library
-
- An executable module which can be shared between multiple applications,
- containing code and/or resources. It is loaded dynamically at run-time rather
- than being statically linked at compile time. This reduces memory requirements
- and allows code independance and code sharing.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Multitasking
-
- Where the CPU shares itself between more than one task, by giving each task a
- slice of processing time.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Workplace Shell
-
- The Workplace Shell is the Object Oriented shell for OS/2.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Application Programming Interface
-
- API is a general term for a set of functions which provide a standard set of
- services to an application. In this article, we refer to the OS/2 APIs as the
- calls to OS/2 and PM which make up the system services in the kernel and the
- GUI.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Parent
-
- The parent-child relationship establishes a hierarchy of windows in the system.
- Child windows are clipped to their parent windows; that is no child window will
- apear outside the boundary of its parent.
-
-
- ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
-
- Owner
-
- The owner of a window will receive messages from the windows it owns.