home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / prgramer / unix / edmi1.inf (.txt) next >
OS/2 Help File  |  1993-02-28  |  121KB  |  2,973 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
  3.  
  4.           Welcome to EDM/2 - The Electronic OS/2 Developers' Magazine!.
  5.  
  6.                   Portions Copyright (C)1993 by Steve Luzynski.
  7.  
  8.                              Issue #1 - March 1993.
  9.  
  10.                            (Press 'Forward' to page.)
  11.  
  12.  
  13. ΓòÉΓòÉΓòÉ 2. Copyright Notice and other Legal Stuff ΓòÉΓòÉΓòÉ
  14.  
  15. EDM/2 Copyright (C)1993 by Steve Luzynski. This publication may be freely 
  16. distributed in electronic form provided that all parts are present in their 
  17. original unmodified form. A reasonable fee may be charged for the physical act 
  18. of distribution; no fee may be charged for the publication itself. 
  19.  
  20. All articles are copyrighted by their authors. No part of any article may be 
  21. reproduced without permission from the original author. 
  22.  
  23. Neither this publication nor Steve Luzynski is affiliated with International 
  24. Business Machines Corporation. 
  25.  
  26. OS/2 is a registered trademark of International Business Machines Corporation. 
  27. Other trademarks are property of their respective owners. Any mention of a 
  28. product in this publication does not constitute an endorsement or affiliation 
  29. unless specifically stated in the text. 
  30.  
  31.  
  32. ΓòÉΓòÉΓòÉ 3. From the Editor ΓòÉΓòÉΓòÉ
  33.  
  34. Hello, and welcome to the first issue of EDM/2, the Electronic Developer's 
  35. Magazine! 
  36.  
  37. This project was the result of my own struggle to get information on 
  38. programming OS/2, particularly in the case of writing Presentation Manager 
  39. programs. What information there is available seems to be spread out amongst 
  40. .INF files, books written for OS/2 1.3, and old magazine articles. My goal was 
  41. to provide a single resource where developers, both new and old, could get 
  42. information on topics ranging from "How do I create a window?" to "How do I 
  43. write an IFS?" 
  44.  
  45. The tone of this magazine may be a bit irreverent at times - not all computer 
  46. programmers are antisocial hermits who only leave their rooms to get more 
  47. Doritos (that's just me). Consider yourself warned. (insert smile here) 
  48.  
  49. In later issues, I'll use this space to actually talk about topics related to 
  50. developing for OS/2. This time, however, I have some administrative business to 
  51. get off my chest... 
  52.  
  53. Contributions 
  54.  
  55. If you have an idea for an article, by all means contact me! Since this 
  56. magazine is targeted at developers of all levels, no topic is too general or 
  57. too esoteric to be appreciated by someone. You can generally assume basic 
  58. familiarity with the C programming language - beyond that, use your judgement. 
  59. Someone reading an article on writing device drivers will probably be more 
  60. advanced than someone reading about resource files. 
  61.  
  62. One area I would like to see covered that hasn't been in this issue is the 
  63. commercial developer's software that is available out there. Reviews of Zortech 
  64. C++, IBM C Set/2, etc. would make nice inclusions. 
  65.  
  66. Money 
  67.  
  68. This magazine is free for the taking. All of the material is copyrighted by the 
  69. person who wrote it - the authors retain all rights to the text as well as 
  70. their code unless stated otherwise. I can't pay them for their efforts, so by 
  71. leaving their work as theirs I leave open the possibility for paid reprints in 
  72. traditional paper works. 
  73.  
  74. Since I really could in no way control the random distribution of this work, 
  75. I'm not even attempting to charge for it. However, if you do feel a deep seated 
  76. desire to pay someone for it, by all means feel free to send your donation: 
  77.  
  78. o  to me. Your money will all be pushed back into the magazine in the form of 
  79.   storage space, Compuserve fees, etc. My address can be located by emailing me 
  80.   at the address listed at the end of this article. 
  81.  
  82. o  to the Free Software Foundation. They are responsible for emx/gcc and a lot 
  83.   more really nice FREE packages available for a variety of machines. They also 
  84.   have administrative overhead just like any other organization. They can be 
  85.   reached at: 
  86.  
  87. Free Software Foundation, Inc.
  88. 675 Mass Ave
  89. Cambridge, MA 02139, USA
  90.  
  91. Etc 
  92.  
  93. This articles presented in this magazine were written by people with a wide 
  94. variety of expertise in the use of IPF (the Information Presentation Facility). 
  95. Because of this, the style of the articles is different for each one - from a 
  96. straightforward scrolling window to multilevel windows with custom icons and 
  97. hotlinks to glossary items. As we all get better at this, eventually everything 
  98. will look as nice as Gavin's article. (editorial smile) Please bear with me 
  99. until then and let me know if you have any suggestions on style or format.  But 
  100. enough from me. Let's get to it. 
  101.  
  102. -Steve Luzynski, Editor. 
  103.  
  104. sal8@po.cwru.edu 
  105.  
  106.  
  107. ΓòÉΓòÉΓòÉ 4. This Month's Features ΓòÉΓòÉΓòÉ
  108.  
  109. o Getting Started with emx/gcc 
  110.  
  111. o The Making of MineSweeper 
  112.  
  113. o Palette Manager 
  114.  
  115. o Advanced GPI:Retained Segments and Transformations 
  116.  
  117.  
  118. ΓòÉΓòÉΓòÉ 4.1. Getting Started with emx/gcc ΓòÉΓòÉΓòÉ
  119.  
  120.                                  Installing EMX
  121.  
  122.                                A Free 'C' Compiler
  123.                                   for OS/2 2.0.
  124.  
  125.  
  126. ΓòÉΓòÉΓòÉ 4.1.1. Introduction ΓòÉΓòÉΓòÉ
  127.  
  128. (Note: This document was originally written by Brooke P. Anderson. It has been 
  129. modified slightly to conform to the .INF format. The original may be obtained 
  130. from the same source as emx itself.) 
  131.  
  132.  Introduction 
  133.  
  134.  This document describes a free GNU software-development system for OS/2 v2.0. 
  135. It tells you how to acquire all of the necessary software, how to install it, 
  136. how to start using it, and where to look for more information. 
  137.  
  138.  The GNU software-development system includes a C and C++ compiler, a debugger, 
  139. an assembler, a make utility (for automating the compilation of programs made 
  140. of many source files), and a hypertext reader (for reading the documentation). 
  141. The compiler generates full 32-bit, optimized code, and it supports all of the 
  142. OS/2 API calls, so you software developers out there can use it for PM 
  143. programming, manipulating semaphores, manipulating threads, using named pipes, 
  144. etc.  Optional packages include curses (a standard library for manipulating 
  145. screenfuls of text and for moving the cursor), a collection of sample programs, 
  146. and full source code for the system. 
  147.  
  148.  GNU software is originally developed by the Free Software Foundation, an 
  149. organization which produces a lot of free software for UNIX.  After the Free 
  150. Software Foundation releases the UNIX versions, people often port them to many 
  151. other operation systems (such as OS/2).  Despite the fact that the software is 
  152. free, the UNIX community considers it a standard and often prefers it over 
  153. other products because of its high quality.  The compilers, for example, 
  154. produce well-optimized code. 
  155.  
  156.  Sometimes, there is more than one port of a GNU program.  For OS/2, there are 
  157. two different ports of the GNU compiler (called "gcc").  This document 
  158. discusses only one of them (the EMX port) since the EMX port provides faster 
  159. floating point routines and works with a debugger.  This document deals with 
  160. version 0.8f of the EMX port, which is the latest version.  People frequently 
  161. produce new versions of and enhancements for the GNU software and the ports 
  162. based on it. 
  163.  
  164.  IMPORTANT:  I have not tested the software on FAT file systems. I think it 
  165. will work, but I have not checked all of the file names to make sure they 
  166. comply with the egregious 8.3 file-naming convention. 
  167.  
  168.  The following topics may be selected by clicking on the individual topics of 
  169. interest or by simply pressing the 'Forward' button on the bottom of the 
  170. window. 
  171.  
  172. How to get emx
  173. How to install emx
  174. UNZ50x32.EXE
  175. WHLINF8F.ZIP
  176. GNUMK362.ZIP
  177. GNUDEV.ZIP, EMXDEV.ZIP, GPPDEV.ZIP, and OBJCDEV.ZIP
  178. The final steps
  179. Using info
  180. Using the compiler
  181. Using the debugger
  182. Using make
  183. Using the assembler
  184. Where to get more information
  185. Conclusions
  186. Optional packages
  187. Sources of distribution
  188. Distributing this document
  189.  
  190.  
  191. ΓòÉΓòÉΓòÉ 4.1.1.1. How to get emx ΓòÉΓòÉΓòÉ
  192.  
  193.  The full software-development system (and the various optional packages 
  194. described later in this document) are available from a variety of sources.  If 
  195. you have access to Internet, you can get the files from anonymous-ftp sites. 
  196. In the USA, the main anonymous-ftp site for OS/2 is ftp-os2.nmsu.edu.  In 
  197. Germany, the main anonymous-ftp site for OS/2 is rusinfo.rus.uni-stuttgart.de. 
  198. Also, see the end of this document for a list of people who are willing to 
  199. distribute the whole system through regular mail. 
  200.  
  201.  You need to obtain the following files (the sizes of the files are listed next 
  202. to the names):  emxdev.zip (620k), gnudev.zip (873k), gppdev.zip (944k), 
  203. gobjcdev.zip (426k), gnumk362.zip (255k), whlinf8f.zip (814k), and unz50x32.exe 
  204. (114k).  This file (emxst31.doc) is available in emxst31.zip (10k). 
  205.  
  206.  On ftp-os2.nmsu.edu, these files are available in 
  207. pub/os2/2.0/programming/emx-0.8f -- except for emxst31.zip, whlinf8f.zip, and 
  208. the correct version of gnumk362.zip, which are temporarily in pub/uploads.  On 
  209. rusinfo.rus.uni-stuttgart.de, check in pub/os2/emx-0.8f. 
  210.  
  211.  
  212. ΓòÉΓòÉΓòÉ 4.1.1.2. How to install it ΓòÉΓòÉΓòÉ
  213.  
  214.  The following subsections describe how to install the various pieces of the 
  215. GNU software-development system.  Go through the procedures step by step as 
  216. described -- the order is important. Don't feel compelled to read through any 
  217. of the readme files or other documentation spit out during the unarchiving 
  218. process -- I think you will have a much easier time if you go through this 
  219. document beforehand. 
  220.  
  221.  
  222. ΓòÉΓòÉΓòÉ 4.1.1.3. UNZ50X32.EXE ΓòÉΓòÉΓòÉ
  223.  
  224.  You will need unzip v5.0 to unarchive the files.  Earlier versions of unzip 
  225. (including PKZip versions earlier than 1.9) will not work. 
  226.  
  227.  To unarchive unz50x32.exe, you simply move it to a convenient directory and 
  228. type "unz50x32".  Add the name of the directory unzip is in to your path.  For 
  229. example, I have unzip in c:\apps\unzip, so I appended "c:\apps\unzip;" to the 
  230. "SET PATH" statement in my config.sys file.  Then reboot.  Now, you can 
  231. unarchive any zip archive by typing "unzip filename", where "filename.zip" is 
  232. the name of the archive. 
  233.  
  234.  
  235. ΓòÉΓòÉΓòÉ 4.1.1.4. WHLINF8F.ZIP ΓòÉΓòÉΓòÉ
  236.  
  237.  Copy whlinf8f.zip to any convenient directory and unarchive it. It will 
  238. disgorge a program called "info.exe" (the hypertext reader), several auxiliary 
  239. files, and a bunch of documentation files for itself, the compiler, the 
  240. debugger, and make. 
  241.  
  242.  
  243. ΓòÉΓòÉΓòÉ 4.1.1.5. GNUMK362.ZIP ΓòÉΓòÉΓòÉ
  244.  
  245.  Copy gnumk362.zip to any convenient directory and unarchive it. The archive 
  246. will create the directory ".\make" and disgorge several files into it.  In 
  247. other words, if you unarchive gnumk362.zip in the directory "\apps", the 
  248. process will create the directory "\apps\make".  You can delete the info 
  249. directory (the one in the make directory) as its contents are duplicated in 
  250. whlinf8f.zip. 
  251.  
  252.  Add the name of the directory make is in to your path statement in config.sys. 
  253. (I have make in c:\apps\make, so I appended "c:\apps\make;" to the "SET PATH" 
  254. statement in my config.sys.) 
  255.  
  256.  
  257. ΓòÉΓòÉΓòÉ 4.1.1.6. GNUDEV.ZIP, EMXDEV.ZIP, GPPDEV.ZIP, and OBJCDEV.ZIP ΓòÉΓòÉΓòÉ
  258.  
  259.  These archives create the directory ".\emx" and a bunch of directories under 
  260. that.  Thus, unarchive the files from the directory in which you want this emx 
  261. directory.  (In other words, if you unarchive gnudev.zip in the directory 
  262. "\apps", the process will create \apps\emx, \apps\emx\bin, \apps\emx\lib, and 
  263. many other such directories.) 
  264.  
  265.  
  266. ΓòÉΓòÉΓòÉ 4.1.1.7. The final steps ΓòÉΓòÉΓòÉ
  267.  
  268.  Now, you need to modify your config.sys file.  The examples below are from my 
  269. config.sys file, and I have the emx directory installed under c:\apps.  Thus, 
  270. my system has the directories "c:\apps\emx\dll", "c:\apps\emx\lib", 
  271. "c:\apps\emx\include", "c:\apps\emx\bin", and so on -- you will need to modify 
  272. the following examples so that the directories are specified correctly. 
  273.  
  274.  First, you need to specify in your libpath the location of emx.dll and other 
  275. dll files.  In my config.sys, I have 
  276.  
  277.  LIBPATH=.;C:\OS2\DLL;C:\OS2\MDOS;C:\; C:\OS2\APPS\DLL;c:\apps\emx\dll; 
  278.  
  279.  Second, add the name of the emx\bin directory to your path statement in 
  280. config.sys.  (I appended "c:\apps\emx\bin;" to the "SET PATH" statement in my 
  281. config.sys.) 
  282.  
  283.  Third, you need to set a few environmental variables so that the compiler 
  284. knows where to find and how to use various files: 
  285.  
  286. set C_INCLUDE_PATH=c:/apps/emx/include
  287. set LIBRARY_PATH=c:/apps/emx/lib
  288. set CPLUS_INCLUDE_PATH=C:/apps/emx/include.cpp;C:/apps/emx/include
  289. set PROTODIR=c:/apps/emx/include.cpp/gen
  290. set OBJC_INCLUDE_PATH=c:/apps/emx/include
  291. set TERM=mono
  292. set TERMCAP=c:/apps/emx/etc/termcap.dat
  293.  
  294.  IMPORTANT:  you need to use forward slashes ("/") and not backward slashes 
  295. ("\") when setting these environmental variables.  However, do NOT use forward 
  296. slashes in your libpath statement. 
  297.  
  298.  Now, reboot your machine so that these definitions take effect. Then, go into 
  299. an OS/2 window and type "cd \apps\emx\lib" (or whatever you would type to get 
  300. to emx\lib), and type "omflib". This completes the installation process. 
  301.  
  302.  
  303. ΓòÉΓòÉΓòÉ 4.1.1.8. Using info ΓòÉΓòÉΓòÉ
  304.  
  305.  Go into the directory in which info.exe resides.  Type "info". You are now 
  306. looking at a screen that has some information on the top half (information such 
  307. as "Typing 'd' returns here, 'q' quits, '?' lists all info commands, 'h' gives 
  308. a primer for first-timers . . .") and a list of subjects near the bottom 
  309. (subjects such as "Info", "Make," "Gcc", "Gdb", etc.). 
  310.  
  311.  Go ahead and type "h" for a tutorial.  The most basic functions to remember 
  312. are:  type "q" to quit, type "?" to get a list of commands, hit the space bar 
  313. or PgDn key to go down a page, press the Del key or the PgUp key to go up a 
  314. page, press "n" to go to the next node (think of a node as being a collection 
  315. of one or more pages dealing with a single topic), press "p" to go to the 
  316. previous node, use the up and down arrow keys to highlight choices of new nodes 
  317. to jump to, and press enter to jump to the highlighted node. 
  318.  
  319.  Just play around with it a little while, and you will get the hang of it. 
  320. Type "d" to get back to the main directory, use the down arrow to highlight 
  321. "Gcc", press enter, press the down arrow to highlight "Contributors", press 
  322. enter, scan through the pages of text by hitting the PgDn key a couple of 
  323. times, type "?" to see a list of commands, type "p" a couple of times, etc. 
  324.  
  325.  
  326. ΓòÉΓòÉΓòÉ 4.1.1.9. Using the compiler ΓòÉΓòÉΓòÉ
  327.  
  328.  To compile a C source file called "myprog.c", type "gcc -o myprog.exe 
  329. myprog.c".  The -o switch tells gcc that it should call the resulting 
  330. executable "myprog.exe".  To compile the C++ source file "myprog.cc", type "gcc 
  331. -o myprog.exe myprog.cc -lgpp".  C++ source files should have the extension 
  332. ".cc".  The -lgpp switch tells gcc to link the C++ libraries.  You can also 
  333. tell gcc to optimize your code by using the -O switch.  There are two levels of 
  334. optimization (-O and -O2, the highest being -O2). Thus, for the 
  335. fastest-executing code (at the expense of time to compile and of size of the 
  336. executable), you would type "gcc -O2 -o myprog.exe myprog.c" for myprog.c and 
  337. "gcc -O2 -o myprog.exe myprog.cc -lgpp" for myprog.cc. 
  338.  
  339.  Note:  Specifying "-o myprog.exe" is important.  If you don't specify ".exe" 
  340. as the suffix of the output file name, the compiler will generate a UNIX-style 
  341. executable which will not run under OS/2 even if you subsequently rename the 
  342. file so that it has a .exe extension. 
  343.  
  344.  
  345. ΓòÉΓòÉΓòÉ 4.1.1.10. Using the debugger ΓòÉΓòÉΓòÉ
  346.  
  347.  To debug a program, you need to compile it with the -g switch: "gcc -g -o 
  348. myprog.exe myprog.c" for myprog.c and likewise for myprog.cc.  After compiling, 
  349. you then type "gdb myprog.exe" to start the debugger.  Type "h" at the debugger 
  350. prompt to get a list of help topics.  gdb supports all sorts of breakpoints 
  351. (including conditional ones), and you can watch variables, set variables to 
  352. different values, etc.  For example, to get help on running programs from 
  353. within the gdb, you can type "help running".  That will give you a list of 
  354. commands such as run, kill, step, etc.  You can get help on individual commands 
  355. by typing, for example, "help step". 
  356.  
  357.  The following is a sample compile and debug session.  Go into the emx\test 
  358. directory and type "gcc -g -o hello.exe hello.cc -lgpp" to compile hello.cc. 
  359. Type "hello" to run it, to see if the compiler is functioning.  The program 
  360. will print "Hello, world!" on the screen.  Now type "gdb hello.exe" to start 
  361. gdb.  At the prompt "(gdb)", type "list main" to list the function "main". Then 
  362. type "break 5" to cause execution to stop at line 5 in the main function.  Type 
  363. "run" to start execution -- it will stop at line 5.  Type "print argc" to see 
  364. the value of the variable "argc".  Type "step" to run line 5 then halt 
  365. execution at the next line.  Type "quit" to quit.  To list a function (main(), 
  366. say) that is longer than a screenful, type "list main"; then type "list" again 
  367. to list the next screenful of main. 
  368.  
  369.  
  370. ΓòÉΓòÉΓòÉ 4.1.1.11. Using make ΓòÉΓòÉΓòÉ
  371.  
  372.  Assume you have a program made of the following source files: "myprog1.c" and 
  373. "myprog2.c".  You might manually compile these files by typing "gcc -o 
  374. myprog.exe myprog1.c myprog2.c".  Of course, if you change only one of the 
  375. files, typing such a command causes the recompiling of both source files. 
  376. During development, this could be tiresome if the files take a long time to 
  377. compile.  A better way would be to type "gcc -c myprog1.c" which compiles 
  378. myprog1.c into the object file "myprog1.o" (the -c switch tells gcc to make an 
  379. object file), then to type "gcc -c myprog2.c" to generate myprog2.o, and 
  380. finally to type "gcc -o myprog.exe myprog1.o myprog2.o".  This way, if you 
  381. change only myprog1.c, you can recompile it and relink it with myprog2.o to 
  382. create myprog.exe (skipping the "gcc -c myprog2.c" step).  This will be much 
  383. faster if the source files take a long time to compile (or if you have a lot of 
  384. source files). 
  385.  
  386.  Of course, doing all this typing is tiresome, too.  Also, if myprog1.c happens 
  387. to depend on myprog1.h, and you change myprog1.h, you must recompile myprog1.c. 
  388. Thus, you have to keep track of all the file dependencies in order to know, 
  389. after changing one header file, which other files need to be recompiled. 
  390.  
  391.  Fortunately, make takes care of all of this automatically.  All you have to do 
  392. is create one text file that describes the various dependencies and the various 
  393. steps to compile the program.  You name the text file "Makefile".  From then 
  394. on, whenever you type "make", make examines the Makefile, looks for files which 
  395. have changed since the last compile, recompiles any files which depend on the 
  396. changed files, and relinks everything into a new executable. 
  397.  
  398.  For example, suppose that myprog.exe is made from myprog1.c and myprog2.c, 
  399. that myprog1.c contains the lines "#include "myprog1.c"" and "#include 
  400. "mainhead.h"", and that myprog2.c includes myprog2.h and mainhead.h.  The 
  401. Makefile describing all of this is 
  402.  
  403. myprog.exe: myprog1.o myprog2.o
  404.    gcc -o myprog.exe myprog1.o myprog2.o
  405.  
  406. myprog1.o: myprog1.c myprog1.h mainhead.h
  407.    gcc -c myprog1.c
  408.  
  409. myprog2.o: myprog2.c myprog2.h mainhead.h
  410.    gcc -c myprog2.c
  411.  
  412.  The first line shows that myprog.exe depends on myprog1.o and myprog2.o.  If 
  413. either of those has changed since the last time make was invoked, make will 
  414. relink them to create myprog.exe by giving the command under the first line. 
  415. The fourth line shows that myprog1.o depends on myprog1.c, myprog1.h, and 
  416. mainhead.h. If any of these three files have changed since the last time make 
  417. was run, make will recompile myprog1.o by issuing the command on line five.  It 
  418. will also realize that myprog.o has changed, that myprog.exe depends on 
  419. myprog.o, and will relink myprog.exe.  If mainhead.h is changed, make will 
  420. recompile and relink everything since myprog1.o needs to be changed, myprog2.o 
  421. needs to be changed, and thus myprog.exe needs to be changed. 
  422.  
  423.  The example above shows the general form of a Makefile.  You give a target 
  424. (like "myprog.exe" or "myprog1.o") followed by a colon, followed by a space, 
  425. followed by a space-delimited list of files the target depends on.  The next 
  426. line specifies the action to be taken when any of the dependencies change:  the 
  427. first character MUST be a tab (not just a bunch of spaces used for 
  428. indentation); then you type the command make should issue.  A Makefile is just 
  429. a list of such targets, dependencies, and actions. 
  430.  
  431.  
  432. ΓòÉΓòÉΓòÉ 4.1.1.12. Using the assembler ΓòÉΓòÉΓòÉ
  433.  
  434.  The compiler provided with this system (gcc) can handle not only C and C++ 
  435. code but assembly as well.  It can also generate assembly language from C or 
  436. C++ source.  To assemble and link assembly-language programs, type "gcc -o 
  437. myprog.exe myprog.s" where "myprog.exe" is the name of the executable and where 
  438. "myprog.s" is the name of the source-code file. Assembly-language source-code 
  439. files should have the extension ".s".  To generate assembly from C code, type 
  440. "gcc -S myprog.c" where "myprog.c" is the name of the C-source-code file -- the 
  441. resulting assembly language will be put into the file "myprog.s". 
  442.  
  443.  
  444. ΓòÉΓòÉΓòÉ 4.1.1.13. Where to get more information ΓòÉΓòÉΓòÉ
  445.  
  446.  The GNU software-development system does not come with documentation like that 
  447. you would get with, for example, Borland C++.  However, emxdev.doc (in the 
  448. emx\doc directory) does contain a complete list of library functions, including 
  449. a list of headers you need to include, what the functions do, and what 
  450. parameters they accept. 
  451.  
  452.  Also, since the C compiler is ANSI-C compliant (or at least close to it) and 
  453. since the C++ compiler is close to AT&T-C++-2.0 compliant, you can use just 
  454. about any reference manuals for ANSI C and AT&T C++ 2.0.  I use the ones I got 
  455. with an old version of Borland Turbo C++.  If you don't have such manuals, you 
  456. should be able to find something suitable in a bookstore.  If you want a C 
  457. reference manual, I recommend C: A REFERENCE MANUAL, by S. P. Harbison and G. 
  458. L. Steele, Jr. (Prentice-Hall, 1991).  If you are just learning C or C++, there 
  459. is a large number of books to choose from, and you shouldn't have any trouble 
  460. finding one that is suitable. 
  461.  
  462.  For those of you developing applications that use the PM or that use special 
  463. OS/2 functions, the system DOES support all of the OS/2 API functions, 
  464. including ones for semaphores, PM programming, named pipes, threads, etc., and 
  465. it supports Kbd, Mou, and Vio functions.  See emxdev.doc (in the emx\doc 
  466. directory) for a list of the supported functions.  The documentation does not 
  467. contain a manual on how to use these API calls -- you need an OS/2 programming 
  468. book for that.  For information on programming the PM, take a look at: 
  469.  
  470. o OS/2 2.0 Presentation Manager GPI: A Programming Guide to Text, Graphics, and 
  471.   Printing, by G. C. E. Winn (Van Norstrand Reinhold, 1992) 
  472.  
  473. o Learning to Program OS/2 2.0 Presentation Manager by Example: Putting the 
  474.   Pieces Together, by Stephen Knight (Van Norstrand Reinhold, 1992). 
  475.  
  476. o Programming the OS/2 Presentation Manager, by Charles Petzhold (Microsoft 
  477.   Press, 1989). 
  478.  
  479.  The book by Petzhold was written for OS/2 1.3, but the information in it is 
  480. still valid. You can also get the IBM redbooks (which are quite economical and 
  481. of which there is a large assortment of titles). 
  482.  
  483.  For using assembly language, the best choice would be a book about 
  484. assembly-language programming in OS/2, supplemented perhaps with a book on 
  485. programming the 80386 or 80486. 
  486.  
  487.  Also, way back when you were unarchiving, you might have been itching to 
  488. examine the various readme files and other documentation.  Now is the time to 
  489. do that to your heart's content.  Browse through the files in the emx/doc 
  490. directory and the information available from the hypertext reader. 
  491.  
  492.  Additional sources of information include GEnie (the OS/2 category of the 
  493. IBMPC bulletin board), Usenet news (the comp.os.os2 newsgroups), and 
  494. CompuServe.  These are places where you can exchange information with other 
  495. people who use and program for OS/2.  In particular, CompuServe is one of the 
  496. official homes for the OS/2 developers' assistance program.  If you are a 
  497. member of the program, IBM will (for only $15) provide you with a CD that 
  498. contains a beta version of the software development kit (including the C and 
  499. C++ compiler and debugger and a full set of on-line documentation), a beta 
  500. version of OS/2 (which contains enhancements such as Windows 3.1 
  501. compatibility), and many other goodies.  For more information, type "go os2dap" 
  502. on CompuServe or call 1-407-982-6408.  The people at 1-800-3-ibm-os2 might also 
  503. be able to provide more information. 
  504.  
  505.  
  506. ΓòÉΓòÉΓòÉ 4.1.1.14. Conclusions ΓòÉΓòÉΓòÉ
  507.  
  508.  I wrote this to help people get started with a free -- yet powerful -- 32-bit 
  509. software-development system for OS/2.  For (at most) the price of a few books, 
  510. you have a full programming system for C++, C, and assembly language.  For the 
  511. additional price of an OS/2 programming book, you have a bargain-basement SDK. 
  512.  
  513.  If you find errors in this document, or if you have suggestions for its 
  514. improvement, please let me know.  My GEnie address is "BROOKE", and my Internet 
  515. address is "brooke@hope.caltech.edu". 
  516.  
  517.  
  518. ΓòÉΓòÉΓòÉ 4.1.1.15. Optional Packages ΓòÉΓòÉΓòÉ
  519.  
  520.  There are three optional packages you can get for this software-development 
  521. system, packages which are not necessary but which can nevertheless be 
  522. important.  All of them are in the pub/os2/2.0/programming/emx-0.8f directory 
  523. on ftp-os2.nmsu.edu. On rusinfo.rus.uni-stuttgart.de, they are probably 
  524. available in a directory such as pub/os2/emx-0.8f. 
  525.  
  526.  The first package contains curses.  Curses is a library of functions that 
  527. allow you to move the cursor around on the screen, manipulate screenfuls of 
  528. text, and get input.  You need the files "bsddev.zip" and "bsddoc.zip".  The 
  529. source code is available in "bsdsrc.zip". 
  530.  
  531.  The second package contains full source code to the software-development 
  532. system.  Most people do not need the source code to everything, but the source 
  533. code to the libraries (i.e., to all the functions) is sometimes useful.  The 
  534. source code to the C libraries is in "emxlib.zip", and the source to the C++ 
  535. libraries is in "gccsrc.zip".  install.doc (in the emx\doc directory) gives the 
  536. names of all the other relevant archives. 
  537.  
  538.  The third package is a collection of sample and test programs that you can use 
  539. to test the compiler and to learn about various aspects of programming.  These 
  540. programs are in the file "emxtest.zip." 
  541.  
  542.  
  543. ΓòÉΓòÉΓòÉ 4.1.1.16. Sources of Distribution ΓòÉΓòÉΓòÉ
  544.  
  545.  This document already described some places from which you can get the 
  546. necessary archives:  ftp-os2.nmsu.edu and rusinfo.rus.uni-stuttgart.de. 
  547. However, some people don't have access to these sites or don't have modems fast 
  548. enough to download megabytes of data in a reasonable amount of time.  For these 
  549. people, I am including the following list of people who are willing to 
  550. distribute the whole system through regular mail. Keep in mind that people 
  551. might change their prices, cease distributing the software, move, etc., so 
  552. contact them first to get current details.  Make sure you ask them if they have 
  553. the latest version of the EMX port (which is currently version 0.8f). 
  554.  
  555. Brooke Anderson
  556. 1155 E. Del Mar #312
  557. Pasadena, CA  91106
  558. USA
  559. Phone:  (818) 577-7555
  560. GEnie:  BROOKE
  561. Internet:  brooke@hope.caltech.edu
  562. Cost:  $18 in US; in other countries, shipping + US$10
  563. Provides:  the basic set of archives and bsddev.zip, bsddoc.zip,
  564. bsdsrc.zip, emxtest.zip, emxlib.zip, and gccsrc.zip.
  565.  
  566. Juergen Egeling
  567. Werderstr. 41
  568. 7500 Karlsruhe
  569. Germany
  570. Phone:  0721-373842
  571. FAX:  0721-373842
  572. BITNET:  ry90@dkauni2
  573. Internet:  ry90@ibm3090.rz.uni-karlsruhe.dbp.de
  574. X.400:  S=ry90;OU=ibm3090;OU=rz;P=uni-karlsruhe;A=dbp;C=de
  575. Cost:  disks + shipping + DM 25
  576.  
  577. Wey J. Ho
  578. Department of Physics
  579. Monash University
  580. Clayton
  581. VIC 3168
  582. Australia
  583. Phone:  +613-565-3615 (or Australia (03) 565 3615)
  584. Fax:  +613-565-3637 (or Australia (03) 565 3637)
  585. Internet:  sci240s@monu6.cc.monash.edu.au
  586. Cost:  disks + shipping + AU$10
  587.  
  588. Doug Robison
  589. 1311 Webster
  590. Chillicothe, MO  64601
  591. USA
  592. Phone:  (816) 646-1085
  593. GEnie:  D.ROBISON
  594. Cost:  disks + shipping + US$5
  595.  
  596.  If you would like to get on this list and if you have access to Internet or an 
  597. on-line service, just send me your name, a description of how people can 
  598. contact you (including your e-mail address), how much money you want for the 
  599. job (such as "$20", "disks + shipping + $30", or whatever you want to charge), 
  600. and what you are offering for that price.  It is helpful to have a list that 
  601. includes people in various countries. 
  602.  
  603.  
  604. ΓòÉΓòÉΓòÉ 4.1.1.17. Distributing this Document ΓòÉΓòÉΓòÉ
  605.  
  606.  I give permission to use, to distribute, and to copy this document freely.  If 
  607. you want to upload it to any bulletin-board or on-line service, please do so. 
  608. I do update this document occasionally (and put the latest version on GEnie and 
  609. ftp-os2.nmsu.edu), so you might want to make sure you have the latest version 
  610. before distributing it. 
  611.  
  612. Brooke Anderson
  613. 1155 E. Del Mar #312
  614. Pasadena, CA  91106
  615. USA
  616. Phone:  (818) 577-7555
  617. GEnie:  BROOKE
  618. Internet:  brooke@hope.caltech.edu
  619.  
  620.  
  621. ΓòÉΓòÉΓòÉ 4.2. The Making of MineSweeper ΓòÉΓòÉΓòÉ
  622.  
  623.                                   The Making of
  624.                                    MineSweeper
  625.  
  626.  
  627. ΓòÉΓòÉΓòÉ 4.2.1. Introduction ΓòÉΓòÉΓòÉ
  628.  
  629. This document and its contents are copyright (C) 1993, by David Charlap. 
  630.  
  631. This document describes some of the design decisions and problems that I 
  632. encountered when making my MineSweeper for OS/2 program.  It assumes a basic 
  633. knowledge of C and Presentation Manager. 
  634.  
  635. I decided to write an OS/2 version of MineSweeper for many various reasons. 
  636. The main reason was to learn a bit more about programming in the OS/2 
  637. presentation manager environment.  I also wanted an OS/2 version of this game, 
  638. since loading the Windows environment is a slow procedure.  Although there is 
  639. already a version of MineSweeper for OS/2 available, I found it to be lacking 
  640. features of the Microsoft game that I wanted. 
  641.  
  642. I chose to develop MineSweeper using the GCC/2 compiler, since I already had it 
  643. installed on my computer at home.  The code I have written should compile 
  644. without change on IBM's C Set/2 compiler or on the EMX/GCC compiler or on any 
  645. other OS/2-compatible compiler. 
  646.  
  647.  
  648. ΓòÉΓòÉΓòÉ 4.2.2. The game ΓòÉΓòÉΓòÉ
  649.  
  650. Before discussing the design, allow me to explain the basic concept of the 
  651. game. 
  652.  
  653. MineSweeper is a game of logic, pitting the player against a clock. A grid of 
  654. squares is presented, some of which contain mines.  All the squares are covered 
  655. at the game's outset.  The object is to discover which covered squares contain 
  656. mines under them using clues provided by the program. 
  657.  
  658. A player uncovers a square by clicking on it with the mouse.  If the square 
  659. contains a mine, the player loses and play stops.  If it does not contain a 
  660. mine, a number is displayed on the square which indicates the number of mines 
  661. that are adjacent to that square.  When all the squares that do not contain 
  662. mines are uncovered, the player wins and play stops. 
  663.  
  664. To prevent mistakes, a player may place a flag on a covered square to indicate 
  665. a suspected mine position.  The computer will not allow a flagged square to be 
  666. uncovered.  The player may also place a question mark on a square to aid in 
  667. visualizing the problem.  The computer will allow a question-marked square to 
  668. be uncovered. 
  669.  
  670. The game is timed. 
  671.  
  672.  
  673. ΓòÉΓòÉΓòÉ 4.2.3. The implementation ΓòÉΓòÉΓòÉ
  674.  
  675. While there are many ways to implement such a game, I chose to copy the 
  676. implementation that Microsoft used for their game.  Partly because I was 
  677. already familiar with it, and partly because it is an intuitive approach to the 
  678. game. 
  679.  
  680.  
  681. ΓòÉΓòÉΓòÉ 4.2.3.1. How the game should behave ΓòÉΓòÉΓòÉ
  682.  
  683. The game is played in a window whose size is directly proportional to the size 
  684. of the game grid.  When the grid's dimensions are changed, the window is 
  685. re-sized to accomodate the new grid.  The top of the window contains the 
  686. current elapsed time, a count of the number of mines left to be flagged, and a 
  687. button that may be clicked for a quick restart of the game.  Below this is the 
  688. game grid. 
  689.  
  690. Covered squares are drawn in a way to resemble buttons. 
  691.  
  692. A user uncovers a square by clicking on it with mouse button 1. When the button 
  693. is depressed over a covered square, the square is drawn as a depressed button. 
  694. If the mouse is dragged over the game grid, the "depressed" button will follow 
  695. the pointer.  When the button is released, the square beneath it will be 
  696. uncovered. 
  697.  
  698. If an uncovered square has no adjacent mines, the program will automatically 
  699. uncover all squares adjacent to it. 
  700.  
  701. Flags and question marks are placed by clicking on covered squares with mouse 
  702. button 2.  The marks are placed as soon as the button is pressed. 
  703.  
  704. As a shortcut, and an aid to solving, mouse button 3 is used to uncover all the 
  705. squares around an uncovered square.  When button 3 is clicked on an uncovered 
  706. square, and there are sufficient flags surrounding the square to satisfy the 
  707. number shown, all remaining squares will be uncovered.  For two-button mouse 
  708. users, pressing buttons 1 and 2 together (chord) will also cause this effect. 
  709.  
  710. When the shortcut button is pressed, the squares surrounding the pointer will 
  711. be drawn in the depressed position.  As the mouse is dragged, the 3x3 area of 
  712. depressed squares will track the mouse.  When button 3 (or either button 1 or 
  713. 2, if the chord is used) is released, the shortcut process will begin. 
  714.  
  715.  
  716. ΓòÉΓòÉΓòÉ 4.2.3.2. How to code this ΓòÉΓòÉΓòÉ
  717.  
  718. Coding this game play was not simple.  My first attempt involved creating 
  719. separate buttons for each square on the playfield.  This way, the user could 
  720. just click on a button, and the program would receive a WM_COMMAND message 
  721. indicating which button was clicked. 
  722.  
  723. This proved difficult for many reasons.  First, it is hard to get a bitmap 
  724. image on the face of a button.  Although I have seen it done, I do not know 
  725. how, and I did not want to take the time to learn. Second, and more important, 
  726. buttons to not behave properly.  Ordinary buttons revert to their "up" state if 
  727. the mouse moves off of them while the mouse button is depressed, but there is 
  728. no facility provided to then depress the adjacent button that the pointer is 
  729. subsequently above.  Also, facilities are not provided to have more than one 
  730. button draw itself on the "down" state when one is clicked on. 
  731.  
  732. In other words, getting the depressed button images to follow the mouse as it 
  733. is dragged across the playfield is difficult to do, if not impossible, using 
  734. button windows. 
  735.  
  736. Instead, I decided to fake it.  I would have multiple bitmaps in memory.  One 
  737. for each possible image that could be drawn on a grid square.  I would then 
  738. just draw the bitmaps on the squares.  If the bitmaps are properly drawn, they 
  739. should look just like buttons.  For making the buttons depress, I would simply 
  740. have additional bitmaps and draw them where appropriate. 
  741.  
  742. There are only a few downsides to this approach.  First of all, bitmaps can not 
  743. be stretched without introducing distortions.  This means that whatever size I 
  744. pick for them is going to be used throughout the game, regardless of user 
  745. preferences.  Second, bitmaps are difficult to re-color when loaded out of 
  746. resource files, so the colors will not necessarily match the system-defined 
  747. button colors. But these are minor concerns, I feel, given the difficulty of 
  748. implementing the game in any other way. 
  749.  
  750.  
  751. ΓòÉΓòÉΓòÉ 4.2.3.2.1. The basic internal structure ΓòÉΓòÉΓòÉ
  752.  
  753. Many variables are needed to keep track of the game.  I chose to make most of 
  754. the critical game variables global, so that they can be accessed from any 
  755. procedure without large amounts of parameter passing.  While this may be poor 
  756. programming practice, it makes a game like this much easier to write and 
  757. understand, in my opinion. 
  758.  
  759. In addition to the expected variables, like the size of the grid and the number 
  760. of mines, three arrays are used.  The arrays are dynamically allocated and 
  761. re-allocated whenever the game grid changes size.  All three arrays correspond 
  762. to the game grid, one element corresponding to each square. 
  763.  
  764. One array is boolean, containing the map of mines - TRUE if the square has a 
  765. mine, FALSE otherwise. 
  766.  
  767. The second array contains the visible screen.  Each element contains a number, 
  768. indicating what the user sees at that coordinate. This array is initialized to 
  769. values corresponding to a blank raised button, but will change as play 
  770. progresses.  This is used primarily for displaying the screen. 
  771.  
  772. The third array contains the count of mines adjacent to each square.  Rather 
  773. than compute the number each time a square is uncovered, the entire set is 
  774. computed in advance.  This way, it's a quick operation to get the correct 
  775. number when a square is uncovered. 
  776.  
  777. Other game variables include an array of bitmap handles for the drawing 
  778. procedures, flags to an end-of-game condition, and the current timer count. 
  779.  
  780.  
  781. ΓòÉΓòÉΓòÉ 4.2.3.2.2. Using bitmaps as buttons ΓòÉΓòÉΓòÉ
  782.  
  783. To implement the game grid, 18 bitmaps would be needed.  I would need a blank 
  784. square, squares with the numbers 1 through 8 on them, a "button" in the raised 
  785. and depressed state, a "button" with a flag on it, a "button" with a question 
  786. mark on it in the raised and depressed state, and bitmaps to indicate errors: 
  787. an exploded mine, a misplaced flag, and an unflagged mine. 
  788.  
  789. All these bitmaps were drawn (on 16x16 16 color bitmaps) using the 
  790. system-supplied icon editor.  These were then all included in the resource file 
  791. with lines like the following in mine.rc: 
  792.  
  793.  BITMAP MINE_BLANK_UP    MineBlankUp.BMP
  794.  BITMAP MINE_BLANK_DOWN   MineBlankDown.BMP
  795.  BITMAP MINE_FLAG      MineFlag.BMP
  796.  BITMAP MINE_QUESTION_UP  MineQuestionUp.BMP
  797.  
  798. Where names like MINE_BLANK_UP are constants defined in the program's header 
  799. file. 
  800.  
  801. Using bitmaps from a program's resources is fairly simple.  First, a 
  802. presentation space is required for drawing into.  While a WinBeginPaint call 
  803. will create a presentation space, I want to be able to draw bitmaps at times 
  804. other than during WM_PAINT message processing.  For this reason, I create a 
  805. presentation space during processing of the WM_CREATE message and store its 
  806. handle in a global variable.  A presentation space may be created outside of 
  807. paint message processing in the following way: 
  808.  
  809.  hdc = WinOpenWindowDC(hwnd);
  810.  sizl.cx = sizl.cy = 0;
  811.  hps = GpiCreatePS(hab, hdc, &sizl, PU_PELS   | GPIF_DEFAULT |
  812.                   GPIT_MICRO | GPIA_ASSOC   );
  813.  
  814. Where hwnd is the window handle passed as part of the WM_CREATE  message, sizl 
  815. is a variable of type SIZEL, and hab is the anchor block created as part of the 
  816. program's WinInitialize function.  The value of hps returned is used for all 
  817. painting operations throughout the program.  It is also passed to WinBeginPaint 
  818. during WM_PAINT message processing to prevent spurious presentation spaces from 
  819. being created. 
  820.  
  821. Once the presentation space exists, the bitmaps are all loaded from the 
  822. program's resources with GpiLoadBitmap calls during the WM_CREATE message 
  823. processing: 
  824.  
  825.  hbitmap=GpiLoadBitmap(hps, NULLHANDLE, ID, BOX_WIDTH, BOX_HEIGHT);
  826.  
  827. Where ID is the resource-ID for the bitmap, as defined in the resource file, 
  828. and BOX_WIDTH and BOX_HEIGHT  are constants indicating the size of the bitmap. 
  829. If the height and width of the bitmaps do not match the constants provided, 
  830. they will be stretched to fit the dimensions requested.  The bitmap handles 
  831. returned in hbitmap are all stored so that they do not need to be loaded again. 
  832.  
  833. Once handles are available for the bitmaps, displaying them is simple, using 
  834. the WinDrawBitmap call: 
  835.  
  836.  WinDrawbitmap (hps, hbitmap, NULL, &ptl, CLR_NEUTRAL, CLR_BACKGROUND,
  837.         DBM_NORMAL);
  838.  
  839. Where hbitmap is the handle of the bitmap requested, and ptl is a POINTL 
  840. structure containing the window-relative coordinate of the lower-left corner of 
  841. the bitmap's target position. 
  842.  
  843. The only other thing to consider is cleaning up when the program terminates. 
  844. Although presentation spaces and handles will be released back to the system 
  845. when the WinTerminate call is placed, it is good practice to release them 
  846. manually, just to be sure nothing is left behind by accident.  (Bugs in new 
  847. systems, like OS/2 can cause this to happen, so it's better safe than sorry.) 
  848. I place all cleanup instructions in the WM_DESTROY message handler.  The 
  849. following statements will delete bitmap handles and presenation spaces: 
  850.  
  851.  GpiDeleteBitmap(hbitmap);
  852.  GpiDestroyPS(hps);
  853.  
  854. Where hbitmap is a valid bitmap handle and hps  is the presentation space 
  855. created earlier.  The GpiDeleteBitmap function should be called repeatedly, 
  856. once for each bitmap handle used in the program. 
  857.  
  858.  
  859. ΓòÉΓòÉΓòÉ 4.2.3.2.3. Using the mouse ΓòÉΓòÉΓòÉ
  860.  
  861. Another significant problem of implementing the desired interface is that of 
  862. getting a depressed "button" to track the mouse.  For example, if the pointer 
  863. is moved from one square to another while button 1 is pressed, the depressed 
  864. button image on the screen should follow it. 
  865.  
  866. For reasons outlined previously, normal buttons won't provide this effect. 
  867. Instead, using bitmaps, the entire effect must be simulated by handling 
  868. appropriate messages.  To properly implement button 1's behavior, we must 
  869. handle the following messages: WM_BUTTON1DOWN, WM_BUTTON1UP, and WM_MOUSEMOVE. 
  870.  
  871. WM_BUTTON1DOWN is generated whenever mouse button 1 is pressed.  This may be 
  872. the left or right button, depending on whether OS/2 is configured for 
  873. left-handed mouse operation or not.  Since we want the left-right buttons to 
  874. swap when in left-handed operating mode anyway, we can simply deal with button 
  875. 1 and not concern ourselves with left or right. 
  876.  
  877. The WM_BUTTON1DOWN message provides the coordinates (window-relative) that the 
  878. pointer is over when the button was pressed.  These are delivered as the high 
  879. and low words of the first message parameter and may be extracted with the 
  880. following macros: 
  881.  
  882.  x = SHORT1FROMMP(mp1);
  883.  y = SHORT2FROMMP(mp1);
  884.  
  885. Before taking action, we must be sure these coordinates are valid. Any time the 
  886. button is pressed while the pointer is within the window's client area will 
  887. cause a WM_BUTTON1DOWN message to be sent, and these coordiantes may not be 
  888. over a segment of the game grid.  ;p.If the coordinates are invalid, we simply 
  889. break from the switch statement, and allow the WinDefWindowProc to handle the 
  890. message. 
  891.  
  892. If the coordinate is valid, we then compute which grid square the coordinate is 
  893. over.  Since the grid is composed of same-sized rectangular regions, we have 
  894. simply to subtract any margins and divide the coordinate by the width (or 
  895. height) of a square to get the identity of the square. 
  896.  
  897. Once the square is identified, we re-draw it in its depressed form. In general, 
  898. the depressed form of a square is identical to it's non-depressed form.  The 
  899. exceptions are the blank covered square and the question-mark covered square. 
  900. These are depressable "buttons", and have different bitmaps for the depressed 
  901. state and the raised state. 
  902.  
  903. A few other steps must also be taken, aside from drawing the depressed button. 
  904. We must set a flag in a static variable indicating that the mouse button is 
  905. down, so that the WM_MOUSEMOVE message handler will know the mouse button's 
  906. position.  We must also store the grid coordiante of the depressed button. 
  907.  
  908. Finally, we must capture the mouse.  When the mouse is captured, the program 
  909. will get all mouse messages, even if the pointer is over some other 
  910. application's space.  This is necessary, since the user may drag the pointer 
  911. outside of our window and release the button there. If this happens, and the 
  912. mouse isn't captured, the program will not receive the WM_BUTTON1UP message, 
  913. and would think that the button is still down. 
  914.  
  915. The mouse is captured and released with the following functions: 
  916.  
  917.  WinSetCapture (HWND_DESKTOP, hwnd);    /* To capture the mouse  */
  918.  WinSetCapture (HWND_DESKTOP, NULLHANDLE); /* To release the capture */
  919.  
  920. Where hwnd is the window handle provided to the message handler. 
  921.  
  922. The WM_BUTTON1UP message is returned when the user releases mouse button 1. 
  923. Compared to the button down handler, this is simple.  First, the capture on the 
  924. mouse is released.  Then, if the coordinate is over a covered-but-not-flagged 
  925. square, we call the uncover function. 
  926.  
  927. If this is the first click of the game, then the timer is started. 
  928.  
  929. The WM_MOUSEMOVE message is sent to the application whenever the pointer 
  930. position changes over the window.  Normally, we will ignore these, passing them 
  931. to the system's handler, WinDefWindowProc, but if button 1 is down, we want to 
  932. take action first. 
  933.  
  934. First, we must check if the pointer's position has moved to another square.  If 
  935. it is over the same square as our saved position, then we do nothing.  If it is 
  936. not over the same square, then we have to draw the previous square in it's 
  937. raised position, and draw the square it's now over in the depressed position 
  938. and save the new coordinate. 
  939.  
  940. Managing the mouse button 2 is simpler, since flags and question marks are 
  941. placed as soon as the button is depressed.  When the WM_BUTTON2DOWN message is 
  942. received and the pointer is over an appropriate square, the array element 
  943. corresponding to the square is changed and the square is re-drawn. 
  944.  
  945. Managing the chord and button-3 sequences are similar to the button 1, except 
  946. that nine boxes must now be drawn in the depressed state instead of one.  This 
  947. only gets a little ugly when the button 1 and button 2 messages must do 
  948. double-duty to manage the chord sequence as well as their individual functions, 
  949. but this is managed easilly with some well-placed if() statements. 
  950.  
  951.  
  952. ΓòÉΓòÉΓòÉ 4.2.3.2.4. Uncovering squares ΓòÉΓòÉΓòÉ
  953.  
  954. Uncoverig a square seems like a simple procedure, but introduces interesting 
  955. problems.  The concept is simple.  The program checks if the square is 
  956. uncoverable and aborts if it is not.  It then checks if a mine is being 
  957. uncovered and ends the game if it is.  Then it does the actual uncovering, 
  958. ending the game if the player wins. 
  959.  
  960. In actuality, it's more complicated than this.  As a courtesey, the program 
  961. should never allow the user to uncover a mine on the first click, so if a mine 
  962. is uncovered on the first click, the program should move that mine elsewhere. 
  963. Also, if a square has no mines adjacent to it, the program should automatically 
  964. uncover all the surrounding squares. 
  965.  
  966. Step one is simple.  Check if the square is covered and does not have a flag on 
  967. it.  If not, just return and do nothing.  There should also be some sanity 
  968. checking here, in case the program tries (in error) to uncover a square that 
  969. isn't on the grid. 
  970.  
  971. For step 2, the program must check if the square has a mine under it.  If so, 
  972. then the program must check if it is the first click. Moving the mine is tricky 
  973. - the program must place a new mine, delete the old mine, and adjust the 
  974. adjacency counters so all the numbers will still read proper values.  Finally, 
  975. it must then loop back to the start and retry uncovering the square. 
  976.  
  977. If it's not the first click, the finding a mine is game over.  The timer is 
  978. stopped and the endgame flag is set, which effectively prevents further play, 
  979. since all of the mouse button message handlers check if the game is over as a 
  980. part of their processing. 
  981.  
  982. If the square does not have a mine, however, it must be uncovered. In which 
  983. case, the count of adjacent mines is fetched.  If there are one or more 
  984. adjacent mines, the bitmap for that number is assigned to that square, and it 
  985. is displayed. 
  986.  
  987. If the uncovered square has zero adjacent mines, all the adjacent squares must 
  988. be uncovered.  At first, I simply had the uncover procedure call itself 
  989. recursively, but I found that very large amounts of empty space would cause 
  990. stack overflow problems, so I had to use an iterative solution, instead.  I 
  991. have a separate procedure to handle this, now. 
  992.  
  993. To iteratively calculate which squares to uncover, the program loops through 
  994. all the squares.  If it finds a covered square that is adjacent to an uncovered 
  995. square with no adjacent mines, it sets the square to its uncovered state 
  996. without calling the uncover procedure. It loops through the grid repeatedly 
  997. until no changes are made to the grid. 
  998.  
  999. The program maintains a count of covered squares at all times.  The count is 
  1000. initialized to the number of squares in the grid and is decremented whenever a 
  1001. square is uncovered.  When the count of covered squares equals the number of 
  1002. mines, a win situation is declared. 
  1003.  
  1004. When the player wins, the timer is stopped, the endgame flag is set, and (if 
  1005. one of the three standard games is chosen) the score is compared to the high 
  1006. score, and if the high score is beaten, the score is recorded and the player 
  1007. may enter his name. 
  1008.  
  1009.  
  1010. ΓòÉΓòÉΓòÉ 4.2.3.2.5. Saving and restoring settings ΓòÉΓòÉΓòÉ
  1011.  
  1012. MineSweeper saves all critical game settings to an initialization file 
  1013. (MINE.INI) when it terminates and restores these settings from this file when 
  1014. restarting.  The window's position, the high scores, and the game settings are 
  1015. among the items saved.  This is all done using OS/2's profile management 
  1016. system. 
  1017.  
  1018. The profile management system is a set of PM calls (all beginning with Prf) 
  1019. that manage INI files.  An INI file is opened by calling PrfOpenProfile, closed 
  1020. by calling PrfCloseProfile, and is read from and written to with other API 
  1021. calls. 
  1022.  
  1023. The calls I used, and the syntax for them are: 
  1024.  
  1025. hini = PrfOpenProfile (hab, "MINE.INI");
  1026. PrfCloseProfile(hini);
  1027. PrfWriteProfileData(hini, pszApp, pszKey, &data, sizeof(data));
  1028. PrfWriteProfileString(hini, pszApp, pszKey, pszString);
  1029. PrfQueryProfileData(hini, pszApp, pszKey, &buf, &buflen);
  1030. PrfQueryProfileSize(hini, pszApp, pszKey, &buflen);
  1031. PrfQueryProfileString(hini, pszApp, , pszKey, NULL, pszString, buflen);
  1032.  
  1033. PrfOpenProfile takes two arguments.  The first is the anchor block that is 
  1034. created when WinInitialize is called. The second is the file name of the INI 
  1035. file.  A variable of type HINI is returned.  This HINI variable is used to 
  1036. reference the INI file for reading and writing. 
  1037.  
  1038. Two preset HINI handles are also available: HINI_USERPROFILE and 
  1039. HINI_SYSTEMPROFILE may be used to access the user and system INI files, which 
  1040. are normally OS2.INI and OS2SYS.INI.  I chose not to use these files, however, 
  1041. since software is required to delete entries from INI files.  By keeping data 
  1042. in a separate initialization file, a user may erase all settings by simply 
  1043. deleting the MINE.INI file. 
  1044.  
  1045. PrfCloseProfile takes one argument: the HINI variable returned by the 
  1046. PrfOpenProfile call.  It closes the profile.  The user and system INI files are 
  1047. not closeable. 
  1048.  
  1049. Data in an ini file is referenced by two keys: and application name and a key 
  1050. name.  These two keys, together, are used to reference arbitrary-sized blocks 
  1051. of data in the INI file.  These blocks may be either strings or binary data. 
  1052.  
  1053. Almost all functions that read or write an INI file take the same first three 
  1054. paramters.  The first being a valid HINI ini handle, the second being a string 
  1055. containing the application name, and the third being the key name. 
  1056.  
  1057. PrfWriteProfileData is used to write binary data to an INI file.  It takes five 
  1058. parameters.  The first three are the standard handle, app name and key name. 
  1059. The fourth is a pointer to the data, and the fifth is the length of the data. 
  1060.  
  1061. PrfWriteProfileString is used to write null-terminated string data to an INI 
  1062. file.  It takes four paramters.  The first three are the standard three, and 
  1063. the fourth is the string. 
  1064.  
  1065. PrfQueryProfileData is used to read binary data from an INI file.  It takes 
  1066. five paramters.  The first three are the standard three.  The fourth parameter 
  1067. is a pointer to a buffer area to hold the data, and the fifth is a pointer to a 
  1068. long variable containing the size of the buffer.  When the call completes, this 
  1069. variable will contain the actual number of bytes transferred. 
  1070.  
  1071. PrfQueryProfileString is used to read string data from an INI file.  It takes 
  1072. six paramters.  The first three are the standar three.  The fourth is a default 
  1073. string that will be supplied if the key can not be found in the INI file; I 
  1074. leave this parameter as a NULL, indicating that I don't want a default string. 
  1075. The fifth paramter is a pointer to the buffer that will contain the string, and 
  1076. the sixth paramter is the maximum string length.  The call will return the 
  1077. actual number of bytes transfered into the buffer. 
  1078.  
  1079. Finally, since I am dynamically allocating storage for these strings, I must 
  1080. find out the length of these strings before I actually read them in, in order 
  1081. to allocate a large enough buffer first.  This is done with the 
  1082. PrfQueryProfileSize API call. 
  1083.  
  1084. PrfQueryProfileSize takes four parameters.  The first three are the usual 
  1085. three, and the fourth is a pointer to a ULONG variable.  This variable will 
  1086. contain the number of bytes that the referenced data block contains. 
  1087.  
  1088.  
  1089. ΓòÉΓòÉΓòÉ 4.2.3.2.6. Sizing the window ΓòÉΓòÉΓòÉ
  1090.  
  1091. In MineSweeper, the mines are always the same size.  And I do not want any 
  1092. margins around the minefield.  This means that the window must be re-sized to 
  1093. fit the minefield. 
  1094.  
  1095. This is a two step process.  First, the proper size must be calculated, then 
  1096. the window must be re-sized to fit. 
  1097.  
  1098. Calculating the window size is trivial, but not immediately obvious. 
  1099. Obviously, the width must be greater than the width of one row of squares, and 
  1100. the height must be greater than the height of one columns of squares.  But this 
  1101. is not enough.  The size of the window includes ALL of the window, including 
  1102. borders, menus, and title bars.  So, the border width must by qyeried from the 
  1103. system and added to the width estimate.  And the menu bar height, the title bar 
  1104. height, and the border height must also be queried and added to the height 
  1105. estimates. 
  1106.  
  1107. These values can be extracted with the WinQuerySysValue function: 
  1108.  
  1109.    menuHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYMENU);
  1110.  captionHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYTITLEBAR);
  1111.  borderHeight = WinQuerySysValue(HWND_DESKTOP, SV_CYBORDER) * 2;
  1112.   borderWidth = WinQuerySysValue(HWND_DESKTOP, SV_CXBORDER) * 2;
  1113.  
  1114. WinQuerySysValue takes two parameters.  The first is a valid desktop handle, 
  1115. which (under OS/2 version 2.0) is always HWND_DESKTOP, and the second is a 
  1116. constant indicating which value to extract.  The following were used: 
  1117.  
  1118. SV_CYMENU               The minimum height for a menu bar.  If the menu font is 
  1119.                         too large, a menu bar will actually become larger than 
  1120.                         this height. Additionally, if a menu bar is too long 
  1121.                         for the window, and wraps onto two or more lines, this 
  1122.                         value will only be the height of one line.  In other 
  1123.                         words, it's not really very accurate. 
  1124. SV_CYTITLEBAR           The height of a titlebar 
  1125. SV_CYBORDER             The height of a thin border.  Multiply this by two to 
  1126.                         get both borders. 
  1127. SV_CXBORDER             The width of a thin border.  Multiply this by two to 
  1128.                         get both borders.
  1129.  
  1130. There are many other system values, but these are the only ones I needed.  The 
  1131. actual width of the window is the width of the window's contents plus the 
  1132. border width.  Unfortunately, the ambiguities of the menu bar's height do not 
  1133. make it that simple to generate the window's height. 
  1134.  
  1135. While there may be better ways to calculate the menu bar's actual height, I 
  1136. chose a quick-and-dirty approach.  I first make a best guess of the window's 
  1137. height, by adding the system height values to the height of the playfield 
  1138. (consisting of the game grid and the score region).  I then set the window to 
  1139. that size with the WinSetWindowPos command.  After that I use the 
  1140. WinQueryWindowRect command to get the actual size of the menu bar.  This works, 
  1141. because everything in OS/2 is a window - I get the window handle for the menu, 
  1142. and then query the window's size - giving me the size of the menu.  Using this 
  1143. size, I re-calculate the height of the window and re-size it if it has changed. 
  1144. This all happens quickly, and no painting occurs during processing, so the user 
  1145. does not see the window change sizes. 
  1146.  
  1147. The window's size and position is set with the following call: 
  1148.  
  1149.  WinSetWindowPos(hwnd, HWND_TOP, x, y, cx, cy, SWP_SIZE | SWP_MOVE |
  1150.          SWP_ZORDER | SWP_SHOW | SWP_ACTIVATE);
  1151.  
  1152. Where hwnd is the window handle.  The second parameter is for placing the 
  1153. window in the stack of open windows; HWND_TOP is a constant that tells the 
  1154. system to put this window above all the others.  x, and y are coordinates for 
  1155. the lower-left hand corner of the window. cx, and cy are the horizontal and 
  1156. vertical sizes of the window.  The last parameter is a set of flags.  The ones 
  1157. presented here tell OS/2 to re-size the window, re-position it, change it's 
  1158. "stack" position, make it visible, and give it focus, respectively. 
  1159.  
  1160. The menu bar's size is fetched with the following calls: 
  1161.  
  1162.  hwndMenu = WinWindowFromID(hwndFrame, FID_MENU);
  1163.  WinQueryWindowRect(hwndMenu, &rcl);
  1164.  
  1165. Where hwndMenu is the window handle of the menu bar, hwndFrame is the window 
  1166. handle of the frame window that owns the menu bar, and rcl is a RECLT structure 
  1167. that contains the dimensions of the window. 
  1168.  
  1169. WinWindowFromID is an API call that returns the window handle of a child 
  1170. window.  In this case, I want to know the handle of a menu-bar window, but I 
  1171. only know the handle of the frame window.  So I use WinWindowFromID to extract 
  1172. the handle.  It takes two parameters.  The first is the handle of the parent 
  1173. window, and the second is the ID of the child.  Menu bars that are created as 
  1174. part of a standard window always have an ID of FID_MENU. 
  1175.  
  1176. WinQueryWindowRect fills in a RECTL structure with the extents of a window. 
  1177. Since the coordinates are window-relative, the bottom and left extents are 
  1178. always 0 and 0, leaving the other two extents containing the width and height 
  1179. of the window. 
  1180.  
  1181.  
  1182. ΓòÉΓòÉΓòÉ 4.3. The Unofficial Guide to the Palette Manager ΓòÉΓòÉΓòÉ
  1183.  
  1184.                               The Unofficial Guide
  1185.                                      to the
  1186.                                  Palette Manager
  1187.  
  1188.  
  1189. ΓòÉΓòÉΓòÉ 4.3.1. The Guide ΓòÉΓòÉΓòÉ
  1190.  
  1191.  IMPORTANT NOTE: If you aren't going to read this whole article, at the very 
  1192. least please read about the FATAL ET4000 bug (search for FATAL ET4000 BUG if 
  1193. you're reading this as ASCII). 
  1194.  
  1195. Credit where it's due: When Windows 3.0 first came out with its Palette Manager 
  1196. in 256-color mode, I fell in love. Finally, a GUI that understood palettes and 
  1197. "did the right thing" when two programs tried to use the palette 
  1198. simultaneously. I was elated to find out that when OS/2 2.0 came out, it would 
  1199. also have a Palette Manager. My elation turned to disgust when I discovered the 
  1200. following paragraph in the READ.ME file for the OS/2 Programmer's Toolkit: 
  1201. Palette Manager functions are available, but no devices currently allow the 
  1202. physical palette to be changed. This means applications attempting to put 
  1203. customized colors into the palette will get the nearest colors from the default 
  1204. palette. 
  1205.  
  1206. As far as I'm concerned, this is tantamount to saying, "You can do anything you 
  1207. want ... as long as you don't want to do anything!" Fortunately, when the 
  1208. Service Pack came out later in 1992, the 32-bit XGA, Trident, and ET4000 
  1209. drivers all included good Palette Manager support (though with a few bugs, one 
  1210. of them serious). Sadly, as of this writing, I believe that people with other 
  1211. 256-color displays (such as 8514/A's and clones) still don't have decent 
  1212. Palette Manager support. 
  1213.  
  1214. So what's Palette Manager, and why should you care? VGA-standard adapters can 
  1215. display any of 262,144 different colors. Unfortunately, you can't display them 
  1216. all at once; you can only see 256 at a time. Fortunately, you can pick any 256 
  1217. of these colors at once (unlike the early CGA, where you had a choice of two 
  1218. sets of four fixed colors). The 256 colors that your adapter is showing at any 
  1219. time make up your physical palette. Under Presentation Manager, your program 
  1220. creates a logical palette. For example, you may want eight shades of red. You 
  1221. ask Palette Manager for eight colors, specifying the RGB values that you want. 
  1222. Palette Manager then gives you eight spaces in the physical palette and sets 
  1223. them to the RGB values you specified. If it runs out of space in the physical 
  1224. palette, then it looks at the colors it couldn't get for you and returns the 
  1225. colors in the physical palette that come closest to matching them. In either 
  1226. case, you don't have to worry about which spots in the physical palette you 
  1227. got; you just draw lines in color 3 (for instance), confident that they will be 
  1228. medium red. 
  1229.  
  1230. (At this point, some of you might say, "Couldn't you do this with a Logical 
  1231. Color Table?" I think the answer is yes [I've never used LCTs myself], but I 
  1232. think Palette Manager is easier to use and more flexible. Also, I'm sort of 
  1233. getting the impression that LCTs are being phased out.) 
  1234.  
  1235. There are other things you can do with Palette Manager. Broadly speaking, there 
  1236. are four different types of tasks you can carry out using Palette Manager: 
  1237.  
  1238. o Examining (but not changing) the physical palette 
  1239.  
  1240. o Creating a logical palette for drawing (e.g., using GpiBox) 
  1241.  
  1242. o Creating a logical palette for displaying a bitmap with special color needs 
  1243.  
  1244. o Creating and animating a logical palette 
  1245.  
  1246. In an ideal world, the third task would just be a special case of the second. 
  1247. However, bugs in the Palette Manager require you to do a few special things to 
  1248. get it to work, so I'll treat it separately. 
  1249.  
  1250. My program PHYSCOLO demonstrates the first task, SCALE demonstrates the second, 
  1251. GRAYBITM demonstrates the third, and ZOOM demonstrates the fourth. Although 
  1252. these programs do very different things, they have many similarities. They (and 
  1253. any other program that uses Palette Manager) carry out the following steps: 
  1254.  
  1255.  1. Make sure Palette Manager is available. 
  1256.  2. Create a PS (and DC) 
  1257.  3. Create the logical palette 
  1258.  4. Select the palette into the PS 
  1259.  5. Realize the palette 
  1260.  6. Use the palette 
  1261.  7. Clean up by deselecting the palette, destroying it, and destroying the PS. 
  1262.  
  1263. Let's look at how we carry out these tasks. At this point, I strongly suggest 
  1264. that you follow along with a copy of PHYSCOLO.C. Either print it out or open it 
  1265. up in another window. 
  1266.  
  1267. STEP ONE: Make sure Palette Manager is available. 
  1268.  
  1269. In the "main" part of PHYSCOLO, you'll find the following block: 
  1270.  
  1271.    {
  1272.       LONG sColors;
  1273.       HDC hdcScr;
  1274.       hdcScr = GpiQueryDevice (WinGetPS (HWND_DESKTOP));
  1275.       DevQueryCaps (hdcScr, CAPS_ADDITIONAL_GRAPHICS, 1, &hasPalMan);
  1276.       hasPalMan &= CAPS_PALETTE_MANAGER;
  1277.       ...
  1278.    }
  1279.  
  1280. (There are two more lines in the actual listing that check how many colors are 
  1281. available on your computer. While all existing drivers with Palette Manager 
  1282. support have exactly 256 colors, this could change in the future.) 
  1283.  
  1284. After running this block, hasPalMan will be zero if the computer doesn't 
  1285. support Palette Manager. PHYSCOLO just keeps going, but it might be more 
  1286. appropriate to give an error message and exit. (See ZOOM, for instance.) 
  1287.  
  1288. So if hasPalMan is nonzero, are you okay? Not necessarily. Remember the OS/2 
  1289. Toolkit READ.ME: OS/2 may report that you have Palette Manager support even if 
  1290. the device won't allow your physical palette to change. Unfortunately, I don't 
  1291. know any way to tell if you "really" have Palette Manager support; if anyone 
  1292. knows, I'd like to hear from them. 
  1293.  
  1294. STEP TWO: Create a PS (and DC) 
  1295.  
  1296. Here's the First Commandment of using Palette Manager: 
  1297.  
  1298. IF YOU'RE GOING TO USE PALETTES, !!DON'T!! USE A CACHED MICRO-PS! 
  1299.  
  1300. If someone had told me this, it would have saved me a couple of weeks. If you 
  1301. only learn two things from this article, learn this and the Fatal ET4000 bug 
  1302. (below). 
  1303.  
  1304. In other words, don't do the following when handling WM_PAINT: 
  1305.  
  1306.    case WM_PAINT:
  1307.       {
  1308.          HPS hps;
  1309.          hps = WinBeginPaint (...);
  1310.          ...
  1311.       }
  1312.  
  1313. Why? When you create a cached micro-PS, it "forgets" everything between 
  1314. WM_PAINT messages. While that's fine for simple graphics, it's not appropriate 
  1315. when you're using a palette, because the cached micro-PS will forget the 
  1316. palette! 
  1317.  
  1318. Instead, we create an uncached micro-PS. Looking at the "ClientWinProc" 
  1319. procedure in PHYSCOLO, we see the following lines: 
  1320.  
  1321.    static HPS hps;
  1322.    static HDC hdc;
  1323.  
  1324. (These are static, of course, since they have to be remembered between calls to 
  1325. ClientWinProc.) Later on, we see the rest of the lines for creating the PS: 
  1326.  
  1327.    case WM_CREATE:
  1328.       {
  1329.          SIZEL sizl;
  1330.          sizl.cx = sizl.cy = 0;
  1331.          hdc = WinOpenWindowDC (hwnd);
  1332.          hps = GpiCreatePS (hab, hdc, &sizl, PU_PELS | GPIF_DEFAULT
  1333.                             | GPIT_MICRO | GPIA_ASSOC);
  1334.       }
  1335.  
  1336. STEP THREE: Create the Palette 
  1337.  
  1338. This step (and Step Five) will differ depending on which task you carry out 
  1339. (e.g., whether the logical palette is for examining the physical palette or for 
  1340. palette animation). In the case of PHYSCOLO, we do the following: 
  1341.  
  1342.    static HPAL hpal;
  1343.    ...
  1344.    WM_CREATE:
  1345.       {
  1346.          ...
  1347.          ULONG tbl [MAX_PAL_SIZE];
  1348.          INT   j;
  1349.          for (j = 0; j < colorsToShow; j++) {
  1350.             tbl [j] = PC_EXPLICIT * 16777216 + j;
  1351.          }
  1352.          hpal = GpiCreatePalette (hab, 0L, LCOLF_CONSECRGB,
  1353.                             colorsToShow, tbl);
  1354.       }
  1355.  
  1356. Four things worth pointing out: 
  1357.  
  1358.  1. Ordinarily, it's a good idea to sort the palette, with the most important 
  1359.     RGB values closer to the beginning of tbl. Here, since PHYSCOLO merely 
  1360.     examines the physical palette, we don't sort entries. 
  1361.  2. The PC_EXPLICIT flag in each tbl entry means, "This number represents an 
  1362.     entry in the physical palette, and NOT a color." So, for instance, 
  1363.     PC_EXPLICIT * 16777216 + 3 means the third entry in the physical palette. 
  1364.  3. The LCOLF_CONSECRGB flag means that tbl is an array of RGB values, each of 
  1365.     which is 4 bytes long (which is why we can "fake" it with ULONGs). 
  1366.     Currently, you must use this flag for all calls to GpiCreatePalette (and 
  1367.     your table must be an array of RGBs / ULONGs). 
  1368.  4. The 0L parameter is appropriate for PHYSCOLO, since all we're doing is 
  1369.     examining the physical palette. Later, I'll try to convince you that it's 
  1370.     appropriate for every other use of GpiCreatePalette, too. 
  1371.  
  1372. STEP FOUR: Select the Palette into the PS. 
  1373.  
  1374. This is easy: 
  1375.  
  1376.      GpiSelectPalette (hps, hpal)
  1377.  
  1378. STEP FIVE: Realize the Palette. 
  1379.  
  1380. You realize the palette when your program changes its logical palette or in 
  1381. response to OS/2's new WM_REALIZEPALETTE message. From what I've seen, 
  1382. WM_REALIZEPALETTE is sent to a Presentation Manager program when that program 
  1383. moves into the foreground, or when some other program changes an (unreserved) 
  1384. palette entry. (Reserved palette entries are ones used for palette animation; 
  1385. see below.) 
  1386.  
  1387. If your program doesn't start in the foreground, it should realize the logical 
  1388. palette before it first draws using that palette. Otherwise, the client area 
  1389. will be drawn in black. That's what the 
  1390.  
  1391.    STATIC BOOL firstPaint = TRUE;
  1392.    ...
  1393.    if (hasPalMan && firstPaint {
  1394.       ...
  1395.       WinRealizePalette (hwnd, hps, &palSize);
  1396.    }
  1397.  
  1398. stuff in the WM_PAINT case is for. 
  1399.  
  1400. The parameters to WinRealizePalette are straightforward, though I wish I 
  1401. understood why it's necessary to pass a pointer to the palette size rather than 
  1402. the palette size itself. 
  1403.  
  1404. The other time PHYSCOLO calls WinRealizePalette is when handling 
  1405. WM_REALIZEPALETTE. When PHYSCOLO receives a WM_REALIZEPALETTE message, it 
  1406. merely calls WinRealizePalette and ignores the return value. This is a little 
  1407. atypical, as I'll explain below. 
  1408.  
  1409. STEP SIX: Use the palette. 
  1410.  
  1411. This is easy. Looking at PHYSCOLO's listing, we see the line 
  1412.  
  1413.    GpiSetColor (hps, j);
  1414.  
  1415. What this means is, use the color of the j-th logical palette entry for your 
  1416. next drawing operation. That's all there is to it. 
  1417.  
  1418. STEP SEVEN: Clean up by deselecting the palette, destroying it, and destroying 
  1419. the PS. 
  1420.  
  1421. When you're completely finished with your palette, you should deselect it and 
  1422. destroy it. When you're completely finished with your PS, you should destroy 
  1423. it. Usually, this will be when your program is ending, which is why all these 
  1424. tasks are handled in the WM_DESTROY code for PHYSCOLO: 
  1425.  
  1426.    if (hasPalMan) {
  1427.       GpiSelectPalette (hps, NULLHANDLE);
  1428.       GpiDestroyPalette (hpal);
  1429.    }
  1430.    GpiDestroyPS (hps);
  1431.  
  1432. This should be completely straightforward. Be sure you get rid of every palette 
  1433. and PS when you're done with it. 
  1434.  
  1435. OTHER USES OF PALETTE MANAGER 
  1436.  
  1437. We've gone through PHYSCOLO and seen how to examine the physical palette. What 
  1438. changes do we have to make to create a custom logical palette for drawing, or 
  1439. for displaying a bitmap, or for animating a palette? The answer is, not many. 
  1440. Most of the changes are in Steps Three and Five. 
  1441.  
  1442. SCALE 
  1443.  
  1444. This program draws a grayscale, or a redscale, or a cyanscale, or .... See 
  1445. SCALE.DOC for details. 
  1446.  
  1447. If we consider how it uses the Palette Manager, SCALE isn't really all that 
  1448. different from PHYSCOLO. It does Steps One and Two (checking for the Palette 
  1449. Manager and creating the PS and DC) the same way that PHYSCOLO does. When we 
  1450. reach Step Three, things are slightly different. In SCALE, the entries in tbl 
  1451. represent RGB triples, and they take the form: 
  1452.  
  1453.      tbl [j] = red * 65536 + green * 256 + blue * 1;
  1454.  
  1455. where "red," "green," and "blue" are the RGB values for the color you desire in 
  1456. logical palette entry j. These values range from 0 (meaning none of that 
  1457. primary) to 255 (meaning the maximum amount of that primary). Here's an 
  1458. example: Bright cyan is formed by mixing the maximum amount of green with the 
  1459. maximum amount of blue. Numerically, this would be 255 * 256 + 255 * 1, or 
  1460. 65535. So, for example, if tbl[3] had a value of 65535, this would mean that 
  1461. the third entry in the logical palette would be bright cyan. (We don't use the 
  1462. PC_EXPLICIT flag since we want to create colors, rather than examine physical 
  1463. palette entries.) 
  1464.  
  1465. Note that I still use 0L in the call to GpiCreatePalette. Why? Well, there are 
  1466. two possible alternative options (which can even be OR'd together): 
  1467.  
  1468. LCOL_PURECOLOR. According to the online docs (in the OS/2 Toolkit), "If this 
  1469. option is set, only pure colors are used and no dithering is done." Well, I've 
  1470. never used this option and I've never seen any dithering. In my honest opinion, 
  1471. Palette Manager does the closest match instead of dithering, since dithering 
  1472. wouldn't work for narrow lines (or single pixels!), and since it'd be too 
  1473. computationally expensive for filled areas. Anyway, you can try this flag if 
  1474. you want; I don't use it and don't plan to. 
  1475. LCOL_OVERRIDE_DEFAULT_COLORS. OS/2 Presentation Manager sets aside about 20 
  1476. colors for the user interface (eg, for the color of Window title bars). This 
  1477. means that you can't ever really get 256 colors. Unless you use this flag, that 
  1478. is. However, using this flag will screw up the look of the user interface. In 
  1479. my honest opinion, it's not worth it; sort your palette and live with the fact 
  1480. that you'll "only" get 236 distinct colors. 
  1481.  
  1482. Step Four is the same old "GpiSelectPalette (hps, hpal)." 
  1483.  
  1484. Step Five is half the same (in handling firstPaint), but half different. In the 
  1485. handling of WM_REALIZEPALETTE, we actually use the return value of 
  1486. WinRealizePalette: 
  1487.  
  1488.       ULONG palSize = colorsToShow;
  1489.       if (WinRealizePalette (hwnd, hps, &palSize)) {
  1490.          WinInvalidateRect (hwnd, NULL, FALSE);
  1491.       }
  1492.  
  1493. What does this do? Basically, when you call WinRealizePalette, it returns the 
  1494. number of palette entry requests that it could NOT meet. If you asked for a 
  1495. palette with 16 colors and it could only give you 13, the other 3 will be 
  1496. matched to the closest available colors, and WinRealizePalette will return 3. 
  1497. In this case, you should redraw your client area, and that's what the 
  1498. "WinInvalidateRect" call does. 
  1499.  
  1500. Step Six, using the palette, is much the same as it was for PHYSCOLO. Notice, 
  1501. however, that SCALE also has a routine to handle things if the machine doesn't 
  1502. even pay lip service to supporting Palette Manager. That's what the 
  1503. "GpiQueryColorIndex" line is about. On a system without Palette Manager, this 
  1504. returns the argument to GpiSetColor that will return the closest match to the 
  1505. RGB value you want. 
  1506.  
  1507. Finally, Step Seven is the same for SCALE as it was for PHYSCOLO. 
  1508.  
  1509. GRAYBITM 
  1510.  
  1511. This program creates a small bitmap with grayscale shapes and displays it on 
  1512. the screen (stretching it to fit the client area). It's pretty much the same as 
  1513. SCALE. There are a couple of differences: It creates the palette before 
  1514. creating a PS, and it uses the logical palette in two different PSes; the one 
  1515. that's used to create the bitmap (hpsMem in the "CreateBitmap" procedure), and 
  1516. the one that's used to display the bitmap (hps in "ClientWinProc"). These 
  1517. differences aren't really that important, though the latter shows that you can 
  1518. "share" palettes by sharing the HPAL. 
  1519.  
  1520. Notice that handling WM_PAINT is simplicity itself: We just call WinDrawBitmap. 
  1521. But why didn't we use GpiBitBlt? And why do the colors get screwed up when we 
  1522. resize (eg, maximize) the GRAYBITM window? We'll tackle these issues when we 
  1523. talk about the bugs in the Palette Manager. 
  1524.  
  1525. ZOOM 
  1526.  
  1527. This program does palette animation. What's that? Well, suppose you do the 
  1528. following: 
  1529.  
  1530. o Create a logical palette with space for four colors, with all four palette 
  1531.   entries set to the window background color. 
  1532. o Draw four non-overlapping filled circles using each of the four colors, with 
  1533.   the circle that uses color 0 on the left, circle 1 to the right of it, and so 
  1534.   on. 
  1535.  
  1536. Okay, so at this point you've drawn four "invisible" circles. Now suppose you 
  1537. set the first entry in the logical palette to blue (or any color that isn't the 
  1538. background). Boom, a circle appears on the left. After a brief pause, change 
  1539. the first color in the logical palette back to the background, and change the 
  1540. second color to blue. Then change the second color to the background and the 
  1541. third color to blue. And so on. If you time these well, this makes it look like 
  1542. an animated sequence of a circle moving from left to right across your client 
  1543. area. But the thing worth pointing out is, you don't have to do any fancy 
  1544. drawing or undrawing in real-time; all you're doing is changing palette 
  1545. entries. Since you're only changing palette entries, this sort of animation can 
  1546. go very fast, once you've finished the complicated initial drawing. 
  1547.  
  1548. ZOOM does something similar: It draws 16 overlapping squares, creates a palette 
  1549. with spaces for 16 colors, and then animates. The result is a little like 
  1550. moving down a long corridor. (For a real cheap thrill, run TESTZOOM.CMD, 
  1551. CTRL-ESC to get the Window List, select all the Zoom sessions, and Tile them. 
  1552. If one of these windows has the focus, click on the desktop to "get rid" of 
  1553. it.) 
  1554.  
  1555. From a programmer's standpoint, ZOOM is yet another straightforward use of 
  1556. Palette Manager. It carries out Steps One and Two in the usual way (though it 
  1557. gives an error message and dies instead of just "trudging on" if it can't find 
  1558. Palette Manager -- since this program exists only to demonstrate palette 
  1559. animation, there's no point in having it continue.) 
  1560.  
  1561. In Step Three, things are slightly different: 
  1562.  
  1563.    tbl [j] = PC_RESERVED * 16777216 + ...
  1564.  
  1565. What's this PC_RESERVED flag? Well, as I said before, if Palette Manager can't 
  1566. give you all the colors you requested, it will use the closest match among the 
  1567. physical palette. (That's why we do a WinInvalidateRect if WinRealizePalette 
  1568. returns a nonzero value.) However, if you use the PC_RESERVED flag, Palette 
  1569. Manager will not match anything with that particular palette entry. This is 
  1570. important if the RGB values of the palette entry will be changing rapidly (the 
  1571. overhead of keeping the closest match up-to-date would be too great). Since 
  1572. changing RGB values rapidly is the heart of palette animation, you should 
  1573. always use the PC_RESERVED flag for palette animation. 
  1574.  
  1575. Step Four is still the same. Steps Five and Six are different. In fact, I'd say 
  1576. that Steps Five and Six are now intertwined, since the way you "use" a palette 
  1577. for animation is to constantly change it, and you have to realize the palette 
  1578. every time you change it. Hence: 
  1579.  
  1580.    ULONG tmp = tbl [0];
  1581.    ...
  1582.    tbl [j] = tbl [j + 1];
  1583.    tbl [shadesToShow] = tmp;
  1584.    GpiAnimatePalette (hpal, LCOL_CONSECRGB, 0, shadesToShow, tbl);
  1585.  
  1586. GpiAnimatePalette is, naturally enough, the function that actually animates 
  1587. (ie, changes the RGB values of) the logical palette. Its arguments are the 
  1588. handle to the palette to change, the ubiquitous LCOL_CONSECRGB, the first index 
  1589. to change, the number of indexes to change, and the table that holds the new 
  1590. RGB values. The call above changes all the palette entries; to change only 
  1591. entries 3 through 7, change the "0" above to "3", and the "shadesToShow" to 
  1592. "5". (The values in tbl would still be the same.) 
  1593.  
  1594. Finally, Step Seven is the same as usual. 
  1595.  
  1596. PALETTE MANAGER BUGS 
  1597.  
  1598. There are several reasons why this guide is unofficial rather than official. 
  1599. One reason is that it describes what I've discovered in the school of hard 
  1600. knocks, which is sometimes quite different from what the official documents 
  1601. say. Another reason is that I don't have any access to official IBM folks, so 
  1602. there may well be errors in this article. I've described how I've used Palette 
  1603. Manager to the best of my ability, but it could be that there are simpler, more 
  1604. elegant ways to carry out some of these tasks. I doubt it, but we'll never know 
  1605. until the IBM documentation improves. 
  1606.  
  1607. (Another reason that this is unofficial is that I have no compunction against 
  1608. slamming IBM when I think they deserve it.) 
  1609.  
  1610. Finally, one of the important reasons why this is an unofficial guide is that 
  1611. I'm going to tell you that the current incarnations of Palette Manager have 
  1612. some real bugs, including one that's very serious. Namely: 
  1613.  
  1614. THE FATAL ET4000 BUG (or, How To Hang Your Computer Using Palette Manager) 
  1615.  
  1616. This is the most important fact in this article; if you forget everything else, 
  1617. remember this. And warn anyone who uses your program about it! 
  1618.  
  1619. If you're running one of the 256-color ET4000 SuperVGA drivers from the Service 
  1620. Pack, AND IF a program changes the physical palette (either an OS/2 program 
  1621. that uses Palette Manager or a seamless WinOS/2 program), THEN !!DON'T MOVE AN 
  1622. ICON ON THE OS/2 DESKTOP!! IF YOU DO, YOUR COMPUTER WILL HANG! 
  1623.  
  1624. Your computer won't be safe until the physical palette has reverted to normal. 
  1625. This won't happen until after the program(s) that changed the palette are 
  1626. finished. And even then, it can take a while; one way to force the palette back 
  1627. is to start PHYSCOLO (or move it to the foreground) and then click on the 
  1628. desktop. The palette will revert to normal. 
  1629.  
  1630. TRIDENT USERS BEWARE! THIS BUG MAY ALSO OCCUR FOR YOU (I couldn't get 
  1631. verification by press time). 
  1632.  
  1633. Fortunately, this bug does NOT occur in the XGA drivers. Neither does it occur 
  1634. in the latest 256-color drivers from the OS/2 2.1 beta (version 6.479), so 
  1635. ET4000 users may want to switch to the beta. I hope the fact that the bug isn't 
  1636. in the beta means that IBM has fixed it, but I haven't heard anything official 
  1637. from them, so take care. 
  1638.  
  1639. Unfortunately, this isn't the only bug in Palette Manager. I know of three 
  1640. others: 
  1641.  
  1642. THE UNIVERSAL BUG: Start a program that uses Palette Manager to draw in its 
  1643. window (eg, SCALE or GRAYBITM). Drop down the system menu over the client area. 
  1644. Click elsewhere to get rid of the menu. The area under the menu won't redraw 
  1645. correctly. This bug occurs on every platform that's known to support Palette 
  1646. Manager, though it's less obvious on XGAs and 2.1 beta-using ET4000s than on 
  1647. Tridents and ET4000s with the Service Pack drivers. 
  1648.  
  1649. THE GPIBITBLT BUG: If you use GpiBitBlt to render a bitmap that uses a logical 
  1650. palette, it'll be drawn with the wrong colors on ET4000s and Tridents. 
  1651. Guaranteed. However, it'll be drawn fine on XGAs. 
  1652.  
  1653. THE WINDRAWBITMAP SCALING BUG: If you use WinDrawBitmap with parameters that 
  1654. require the bitmap to stretch or shrink, it'll be drawn with the wrong colors 
  1655. on ET4000s and Tridents. Again, though, it'll look fine on XGAs. 
  1656.  
  1657. I have sent bug reports from IBM on all of these, as well as sample programs 
  1658. that demonstrate the GpiBitBlt bug. However, I'm just one voice crying in the 
  1659. wilderness: If you want to see these bugs fixed, PLEASE get the file 
  1660. OS2PROB.TXT from the /pub/os2/2.0/info directory on ftp-os2.nmsu.edu, fill it 
  1661. out, and e-mail it to 76711.610@compuserve.com. Make sure that you only report 
  1662. ONE bug on each form; otherwise, you'll just get a note back from IBM asking 
  1663. you to resubmit your report on several forms. As long as I am the only person 
  1664. asking for these bugs to be fixed, they'll probably be a low priority for IBM. 
  1665.  
  1666. FINAL THOUGHTS 
  1667.  
  1668. Don't let these bugs get you down; Palette Manager is a fine subsystem that 
  1669. lets you accomplish a lot of great effects. My hat's off to IBM for giving us 
  1670. such a simple and elegant way to use palettes, and my hat's off to YOU for the 
  1671. fine Palette-Manager-using programs that I hope you'll be writing soon ;-). 
  1672.  
  1673. Raja Thiagarajan / sthiagar@bronze.ucs.indiana.edu / 2-21-93 
  1674.  
  1675.  
  1676. ΓòÉΓòÉΓòÉ 4.4. Advanced GPI:Retained Segments and Transformations ΓòÉΓòÉΓòÉ
  1677.  
  1678.                                   Advanced GPI:
  1679.                                 Retained Segments
  1680.                                        and
  1681.                                  Transformations
  1682.  
  1683.  
  1684. ΓòÉΓòÉΓòÉ 4.4.1. Advanced GPI ΓòÉΓòÉΓòÉ
  1685.  
  1686. [The code presented in this article is contained in GPI.ZIP -Ed.] 
  1687.  
  1688. When you say "Graphic Program Interface" what first comes to mind is a set of 
  1689. about a dozen and a half system calls to draw lines, boxes, circles, and other 
  1690. kinds of graphic elements.  But, I may ask you, did you know that by using OS/2 
  1691. Presentation Manager, you could refresh your entire window, whatever you draw, 
  1692. with one call?  Did you know that you can take graphic "segments" and do things 
  1693. like rotate them, translate them, and scale them, without actually having to 
  1694. write the code to do the matrix math yourself?  Well, thats what we're going to 
  1695. be talking about.  There is a whole host of functions that are built into the 
  1696. GPI of OS/2's presentation manager that perform these features, plus more. 
  1697. Unfortunately, these functions are not normally used by the common user, and 
  1698. thats why I'm going to be talking about them here.  This is an introduction to 
  1699. the Advanced GPI functions that many of us don't usually think about. 
  1700.  
  1701. Just as an introduction, I'm assuming that you've programmed OS/2 presentation 
  1702. manager programs before.  Therefore, I'm not going to go into the specifics of 
  1703. the calls such as WinCreateStdWindow, or any of the other Window functions, 
  1704. since this article mainly deals with Gpi functions.  But, if you've never 
  1705. programmed PM before, I do think that you should still be able to follow this 
  1706. article through to the end, and by looking at the source code, which I hope is 
  1707. clear and straightforward, you should be able to pick up with what I'm talking 
  1708. about.  So, I would also recommend that you have a copy of the source code 
  1709. around while you're reading this, since I'll be talking about distinct parts of 
  1710. the code, and to get a better view of whats going on, you should see the 
  1711. context of how the call is used. 
  1712.  
  1713. First, we're going to talk about presentation spaces.  What exactly is a 
  1714. presentation space and how can we use it to help us?  Very generally, a 
  1715. presentation space is the part of the system that takes your calls to the GPI 
  1716. functions, and generates the pictures that you see on the screen.  In most 
  1717. cases, this mapping of graphic calls to displayed results is normally a 
  1718. pass-through operation, that is, the things that you do aren't retained in 
  1719. memory, other than the results that you see on the screen.  This is the default 
  1720. behavior of the presentation space that is associated with the client window 
  1721. that would be created by a WinCreateStdWindow call, this type of presentation 
  1722. space is called the "Cached Micro-PS."  But, there are other types of 
  1723. presentation spaces, the most important of these being the "Normal PS."  What a 
  1724. Normal PS allows you to do is take the graphic calls that you're sending it, 
  1725. and remember them for later operations, like refreshing the window, or rotating 
  1726. your objects, or whatever you like.  A Normal PS also supports correlation 
  1727. functions that will let you know if any of your graphic segments pass through a 
  1728. certain rectangle of the screen, that you define.  This correlation is 
  1729. wonderful for user interface tasks, such as clicking on an object to select it. 
  1730. We'll see how to use these functions later in the article. 
  1731.  
  1732. The program that is developed here displays nine objects on the screen, each of 
  1733. which is its own segment in the presentation space. By clicking on an object, 
  1734. with either the right or left button, you can select an object.  When an object 
  1735. is selected, you can use the '+' and '-' keys to rotate it, or drag with the 
  1736. right mouse button to translate it.  The '=' key does the same thing as the '+' 
  1737. key, for ease of use. 
  1738.  
  1739. Since we know that the default Presentation Space create with the 
  1740. WinCreateStdWindow call isn't a Normal PS, we have to figure out how to create 
  1741. the PS that we really want.  Looking through our list of the GPI functions, we 
  1742. might take notice of GpiCreatePS. 
  1743.  
  1744. HPS GpiCreatePS(HAB hab,HDC hdc,PSIZEL pszl,LONG loptions)
  1745.  
  1746. "hab" is the Handle to our Anchor Block that we got when we did our first 
  1747. WinInitialize call.  This should be readily available as a global variable to 
  1748. our program.  HDC is the Handle of the Device Context that is associated with a 
  1749. window, or in layman's terms, a handle that describes what type of device we're 
  1750. working with.  This is readily available by calling the WinOpenWindowDC 
  1751. function.  So, all we need now are the pszl that defines the size of the 
  1752. presentation space that we want to create, and some options.  Looking at the 
  1753. functional description of the call, we see that if we specify (0,0) as the size 
  1754. of the PS we're creating, a default size for our device will be used instead. 
  1755. So, we're left with some options, that describe what type of PS we're going to 
  1756. be creating.  In our case, we want the Page Units to be pixels (since we're 
  1757. drawing on the screen), we want a Normal PS, and we want to asociate the PS 
  1758. with the hdc that we're dealing with, so we tell it to automatically do this 
  1759. for us.  So, our final version of the call ends up being: 
  1760.  
  1761. sizel.cx=0;
  1762. sizel.cy=0;
  1763. hps=GpiCreatePS(hab, hdc, &sizel, PU_PELS | GPIF_DEFAULT
  1764.                 | GPIT_NORMAL | GPIT_ASSOC);
  1765.  
  1766. And, we can use this HPS throughout our program, as long as we follow a couple 
  1767. simple conventions, the main one being that we destroy it at the end of our 
  1768. program.  Normal PS's take up a lot of memory, and you don't want to be using 
  1769. them where you don't have to.  Use a normal PS only when you want to use the 
  1770. functions that go along with it.  You'll quickly run out of memory if you use a 
  1771. normal PS for every window that you create. 
  1772.  
  1773. Looking at our WM_CREATE message in our client window procedure, we see the 
  1774. above call to GpiCreatePS, along with a couple other suspicious looking calls, 
  1775. the first of these being the GpiSetDrawingMode command. Before explaining the 
  1776. call, I'll explain a little more about the process that will be going on in the 
  1777. program. 
  1778.  
  1779. We know that we have now a Normal PS instead of a Cached Micro PS and we know 
  1780. that we can have retained graphics using this normal PS. Well, it would seem 
  1781. fairly obvious that there has to be some way of more readily organizing the 
  1782. information (graphics) that we're going to be retaining.  What the Gpi can do 
  1783. for us is break our individual calls to Gpi primitives into larger blocks, each 
  1784. of which is a segment.  Each segment has a "tag" that goes along with it.  This 
  1785. "tag" is just a number that we assign to the segment so that we can remember 
  1786. which one is which.  The segments also have numbers, and we'll order them 
  1787. sequentially.  In our program, the segment number and the tag are the same 
  1788. number, for simplicity's sake.  Note that to do the functions that we're doing, 
  1789. each segment must have its own unique non-zero tag.  Along with the tag, a 
  1790. segment transform matrix is also stored.  If you're not familiar with computer 
  1791. graphics and transform matrices, just think of this as something that will 
  1792. specify the position and rotation angle of the segment.  It can also specify 
  1793. the scaling value of a segment, but we won't deal with those calls in this 
  1794. example program.  The calls to the scaling routines are very similar to the 
  1795. ones for translation and rotation, so if you have a reference manual that 
  1796. describes these functions, you should be able to use them with no problem.  So, 
  1797. we've got our segments.  For this program, we're going to have nine segments. 
  1798. Two will be five pointed stars, one a square with rounded corners, one an oval, 
  1799. and the rest will be squares.  They'll be arranged in a 3x3 grid on the screen. 
  1800. We also must mote that matrices in OS/2 are mainly composed of fixed point 
  1801. numbers instead of floating point.  This allows the Gpi to not require a 
  1802. floting point coprocessor and still run at a resonable speed on slow computers. 
  1803. Thus, there is a macro called MAKEFIXED that takes two arguments, an integer 
  1804. portion and a fractional portion of the number. As a little background, to 
  1805. represent a number as a fixed point, call our number 'n'.  You would use the 
  1806. following code: 
  1807.  
  1808. i=(INT)n;
  1809. f=(n*65536)-((INT)n*65536);
  1810. fixed=MAKEFIXED(i,f);
  1811.  
  1812. So, back to talking about the GpiSetDrawingMode command.  What this does is 
  1813. tell our presentaion space to do one of the following things: Draw only, draw 
  1814. and retain, or retain only.  We're going to be using the retain only function, 
  1815. so that we can enter the segments into the presentation space, then draw them 
  1816. ourselves with other specialized calls later.  So, we use the call 
  1817.  
  1818. GpiSetDrawingMode(hps,DM_RETAIN);
  1819.  
  1820. To set the drawing mode.  The other suspicious looking call is the 
  1821. GpiSetPickApertureSize call.  I'll come back to this later.  What it does is 
  1822. set the size of the rectangle that we'll be using as our correlation area. 
  1823.  
  1824. Now, the last thing that's there is the SetupSegments call.  This is a 
  1825. procedure that was written to draw the nine segments, and set their initial 
  1826. segment transform matrices so that they're not rotated, and so that they're 
  1827. arranged in a 3x3 grid.  Looking at this procedure, we see some calls that look 
  1828. somewhat unfamilar. GpiSetInitialSegmentAttrs.  Oh, this must have to do 
  1829. something with the segment attributes.  Yes, it does.  Every segment has some 
  1830. flags that go along with it, this just tells the system "for every new segment 
  1831. that you make, set these options from now on" The options that are most 
  1832. commonly used are: 
  1833.  
  1834.  1. ATTR_VISIBLE         The segment will be visible -- i.e. will be drawn 
  1835.  
  1836.  2. ATTR_DETECTABLE       The segment will be detectable by the correlation 
  1837.     functions 
  1838.  
  1839.  3. ATTR_CHAINED         The segment will be in the segment chain. 
  1840.  The segment chain is the list of segments that will be drawn by the 
  1841. GpiDrawChain call.  So, in our case, we want all of our segments to be in the 
  1842. segment chain.  Now, the second unusual looking call is the GpiScale call. 
  1843. What we're doing here is initializing the variable 'identity' so that it 
  1844. contains the identity matrix, so that we can set our viewing transformation to 
  1845. be the identity.  What this means is that if we say our square is 30x30, it 
  1846. will come out as 30 pixels on a side, exactly.  So, we call 
  1847. GpiSetViewingTransformMatrix with the appropriate arguments.  The one that 
  1848. might look suspicious is the second one, the 9L.  This specifies that there are 
  1849. nine elements in our matrix.  This is because for two dimensional 
  1850. transformations, you deal with 3x3 matrices.  The rest of the code is organized 
  1851. as follows: 
  1852.  
  1853. GpiOpenSegment();
  1854. GpiSetTag();
  1855. GpiSetColor();
  1856. GpiTranslate();
  1857. GpiSetSegmentTransformMatrix();
  1858. .
  1859. .
  1860. .
  1861. Draw our segment
  1862. .
  1863. .
  1864. .
  1865. GpiSetModelTransformMatrix();
  1866. GpiCloseSegment();
  1867.  
  1868. What we're doing is keeping a counter of the segments that we've drawn, so that 
  1869. each segment will have a unique number.  We open that segment, set its tag, and 
  1870. its color.  The GpiTranslate call sets the matrix 'translate' to be matrix that 
  1871. will translate the image drawn from (0,0) to the point specified in 
  1872. ptlTranslate.  Then, using the GpiSetSegmentTransformMatrix call, we set the 
  1873. matrix for the current segment, to be the translation matrix that we just 
  1874. computed.  Along with the individual segment transform matrices for each 
  1875. segment, there is a model transform matrix which is applied to all segments 
  1876. drawn. When a segment is drawn, its transform matrix gets multiplied to the 
  1877. model transform matrix, resulting in an accumulation of the transforms.  To get 
  1878. around this effect, at the end of each segment we set the model transform to be 
  1879. the identity.  We then close the segment we're working with. 
  1880.  
  1881. So, after the SetupSegments call, we've got nine retained segments, each with 
  1882. its own individual transform matrix to put it in the right spot on the screen. 
  1883. The hard part is through.  We've already drawn the innards of our segments, an 
  1884. operation that only has to happen once, and we've also set their initial 
  1885. values, something that only has to happen once.  From here on out, its just the 
  1886. accumulation of the deltas that make up the other operations we can do on the 
  1887. segments. 
  1888.  
  1889. Looking at the WM_PAINT messaage, which is the next things that happens, we see 
  1890. that it is very short and concise.  We erase the presentation space, set the 
  1891. forground mix to XOR mode, so that we can easily erase a segment once we've 
  1892. drawn it, then we call the infamous GpiDrawChain, which draws all the chained 
  1893. segments, along with applying their individual transform matrices.  Note that 
  1894. the WinBeginPaint call in our WM_PAINT message is probably a little different 
  1895. than those that you've seen before.  Because WinBeginPaint usually returned a 
  1896. hps, but we have a PS already, we have to tell it that, so the second argument 
  1897. to the call is our current hps.  This way it doesn't get obliterated by the new 
  1898. PS that would have been created. 
  1899.  
  1900. The next message that I'll look at is the WM_BUTTON?DOWN messages, they're both 
  1901. the same, so I'll just talk about WM_BUTTON1DOWN.  What we're going to be doing 
  1902. here is correlating the retained segments to see if we've clicked on one or 
  1903. not.  To do this we have to do a couple of setup things first.  Intuitively, we 
  1904. have to set the size and position of the rectangle that we're going to be 
  1905. correlating with, then do the actual correlation.  Because the size of the 
  1906. correlation rectangle doesn't change throughout the program, I've placed it in 
  1907. the WM_CREATE message, so it only gets executed once.  Take a look at the code, 
  1908. it sets the variable 'size' so that its 10 pixels square, then calls 
  1909. GpiSetPickApertureSize.  This calls are fairly obvious, first the hps, then the 
  1910. options, then the pointer to the variable 'size'. The option PICKAP_REC tells 
  1911. the Gpi system that we want to use the value specified in the third argument, 
  1912. as opposed to using some default vaule.  Now for the position of the pick 
  1913. aperture.  Back to the WM_BUTTON1DOWN message, we see that the variable 'ptl' 
  1914. is set to be the current position of the mouse, and we call 
  1915. GpiSetPickAperturePosition.  Its arguments are self explanatory.  Next comes 
  1916. the neat call, the correlation. 
  1917.  
  1918. lNumHits=GpiCorrelateChain(     HPS hps,
  1919.                                 LONG lType,
  1920.                                 POINTL pptlPick,
  1921.                                 LONG lMaxHits,
  1922.                                 LONG lMaxDepth,
  1923.                                 PLONG alSegTag);
  1924.  
  1925. lType specifies what type of segments you want to correlate with.  In our case, 
  1926. we want to do all segments, so we specify PICKSEL_ALL.  The pptlPick argument 
  1927. is again the position of the pick aperture. lMaxHits and lMaxDepth together 
  1928. specify the maximum number of hits that will be recorded by this function.  In 
  1929. my case, as in most cases, both of these arguments will have the value one, 
  1930. since we only are interested in one segment at a time.  The PLONG alSegTag is 
  1931. an output array of segment number and tag value pairs.  For example, on return, 
  1932. if we've hit a segment alSegTag[0] will be the segment number of the selected 
  1933. segment, and alSegTag[1] will be its tag vaule.  The routine returns the number 
  1934. of segments hit, so in our case, this will be either zero or one, and we're 
  1935. only interested in the case where its nonzero.  If the call returns nonzero, we 
  1936. set our variable 'selected' to be the index of the currently selected segment, 
  1937. and continue, otherwise we set 'selected' to be zero, to signify that nothing 
  1938. is currently selected. 
  1939.  
  1940. Now, when we get a WM_MOUSEMOVE message, and when button two is down, we do 
  1941. some more magic.  What we want to accomplish here is to figure out how far the 
  1942. mouse was moved at each step, and then add that increment to the current 
  1943. position of the currently selected segment. The first thing that we do is draw 
  1944. the segment in XOR mode, which will erase it from the screen. We compute the 
  1945. position delta, and then use GpiTranslate to get the matrix that represents 
  1946. that translation. Using GpiSetSegmnetTransformMatrix with the TRANSFORM_ADD 
  1947. parameter, we then add this delta translation to the total translation.  The 
  1948. TRANSFORM_ADD parameter tells the Gpi to postmultiply the matrix onto the 
  1949. current matrix.  Because we want all rotations to come before all translations, 
  1950. we postmultiplt translations, and premultiply rotations. The last thing that we 
  1951. do here is draw the segment again, so that it appears in its new place.  The 
  1952. GpiDrawSegment call draws just one segment, which is specified as the second 
  1953. argument to the call. 
  1954.  
  1955. The last part of our program that we havn't looked at yet is the WM_CHAR 
  1956. message.  It too is fairly simple.  It sets the rotation angle to be 3.0 
  1957. degrees, then if the '-' key has been pressed, negates this value.  It then 
  1958. premultiplies the right matrix onto the segment transform matrix with the 
  1959. GpiRotate and GpiSetSegmentTransformMatrix calls.  Note that the parameter to 
  1960. GpiSetSegmentTransformMatrix is TRANSFORM_PREEMPT. 
  1961.  
  1962. I've described all the Gpi functions that allow you to create segments, and 
  1963. modify the segment transform matrices so that you can rotate and translate 
  1964. them.  Hopefully I've given enough background so that you can write your own 
  1965. programs that use these advanced Gpi functions, along with the others that deal 
  1966. with retained segments and segment transform matrices.  In most cases, these 
  1967. calls aren't as hard as they seem, and it is guaranteed that using these calls 
  1968. will both save you programming time, and increase the speed of your own code. 
  1969. Have fun! 
  1970.  
  1971.  
  1972. ΓòÉΓòÉΓòÉ 5. Columns ΓòÉΓòÉΓòÉ
  1973.  
  1974. o Questions and Answers 
  1975.  
  1976. o Introduction to PM 
  1977.  
  1978. o Project Barrel 
  1979.  
  1980.  
  1981. ΓòÉΓòÉΓòÉ 5.1. Questions and Answers ΓòÉΓòÉΓòÉ
  1982.  
  1983.                                     Questions
  1984.                                        and
  1985.                                      Answers
  1986.  
  1987.  
  1988. ΓòÉΓòÉΓòÉ 5.1.1. Introduction ΓòÉΓòÉΓòÉ
  1989.  
  1990. Welcome to the first "Questions and Answers"!  Each month, I collect various 
  1991. questions sent to me via email and try to answer each directly; the ones that I 
  1992. feel contribute the most to developers, whether in terms of information or as a 
  1993. nifty trick to tuck into your cap, get published in this column. 
  1994.  
  1995. To submit a question, send mail to my email address - os2man@panix.com - and be 
  1996. sure to grant permission to publish your question (those that forget will not 
  1997. be considered for publication). 
  1998.  
  1999.  
  2000. ΓòÉΓòÉΓòÉ 5.1.1.1. "Smart icons" ΓòÉΓòÉΓòÉ
  2001.  
  2002. Stefan Gruendal (Stefan_Gruendel@wue.maus.de) writes: 
  2003.  
  2004. But to my question:  How do I build my own "smart icons", i.e. bitmaps on 
  2005. pushbuttons, that optically "move into the screen"? I didn't find any way to 
  2006. achieve this with the Toolkit's Dialog Editor.  But as mentioned above, I know 
  2007. there's a way. 
  2008.  
  2009. Starting with OS/2 2.0, a new button style was added - BS_ICON - which allows 
  2010. you to do what you are trying to accomplish.  It works, as far as I know, only 
  2011. for pushbuttons, and the icon or bitmap is taken from the resource file, whose 
  2012. resource id is specified in the pushbutton text. 
  2013.  
  2014. For example: 
  2015.  
  2016. In FOO.H: 
  2017.  
  2018. #define IDBM_BUTTON 256
  2019. #define IDPB_BUTTON 257
  2020.  
  2021. In FOO.RC: 
  2022.  
  2023. BITMAP IDBM_BUTTON BUTTON.BMP
  2024.  
  2025. In FOO.C: 
  2026.  
  2027. sprintf(achText,"#%d",IDBM_BUTTON);
  2028.  
  2029. hwndButton=WinCreateWindow(hwndClient,
  2030.                            WC_BUTTON,
  2031.                            achText,
  2032.                            WS_VISIBLE|BS_PUSHBUTTON|BS_ICON,
  2033.                            10,
  2034.                            10,
  2035.                            32,
  2036.                            32,
  2037.                            hwndClient,
  2038.                            HWND_TOP,
  2039.                            IDPB_BUTTON,
  2040.                            NULL,
  2041.                            NULL);
  2042.  
  2043. The bitmap is stretched or compressed to fill the button. 
  2044.  
  2045.  
  2046. ΓòÉΓòÉΓòÉ 5.1.1.2. Finding yourself ΓòÉΓòÉΓòÉ
  2047.  
  2048. Raja Thiagarajan (sthiagar@bronze.ucs.indiana.edu) writes: 
  2049.  
  2050. Can a PM program tell if there's a previous instance of itself running?  In 
  2051. Win3.x (but apparently NOT Win32), there's a hPrevInst handle; is there an OS/2 
  2052. 2.0 equivalent?  Basically, I'm thinking in terms of a program that would try 
  2053. to borrow resources from a previous instance if a previous instance is running. 
  2054. (Specifically, if my palette animation program gets started twice, the second 
  2055. instance oughta share palettes with the first instance!) 
  2056.  
  2057. What you're really asking is two questions: 
  2058.  
  2059.  1. How can I determine if a previous instance of my application is already 
  2060.     running? 
  2061.  2. How can I share resources between multiple instances of my application? 
  2062.  
  2063. To answer your first question, you need to enumerate all of the main windows 
  2064. present on the desktop, and figure out if any of them are yours.  This is 
  2065. achieved using the following code: 
  2066.  
  2067. HWND queryAppInstance(PCHAR pchClassWanted)
  2068. {
  2069.    HENUM heEnum;
  2070.    HWND hwndTop;
  2071.    HWND hwndClient;
  2072.    CHAR achClass[256];
  2073.  
  2074.    heEnum=WinBeginEnumWindows(HWND_DESKTOP);
  2075.  
  2076.    hwndTop=WinGetNextWindow(heEnum);
  2077.    while (hwndTop!=NULLHANDLE) {
  2078.       hwndClient=WinWindowFromID(hwndTop,FID_CLIENT);
  2079.       if (hwndClient!=NULLHANDLE) {
  2080.          WinQueryClassName(hwndClient,sizeof(achClass),achClass);
  2081.          if (strcmp(achClass,pchClassWanted)==0) {
  2082.             WinEndEnumWindows(heEnum);
  2083.             return hwndClient;
  2084.          } /* endif */
  2085.       } /* endif */
  2086.  
  2087.       hwndTop=WinGetNextWindow(heEnum);
  2088.    } /* endwhile */
  2089.  
  2090.    WinEndEnumWindows(heEnum);
  2091.    return NULLHANDLE;
  2092. }
  2093.  
  2094. To answer your second question, the only way that I know of to share resources 
  2095. is to place them in a DLL.  The procedure to do this is as follows: 
  2096.  
  2097. o Create a dummy source file with an empty procedure in it. 
  2098. o Compile the source file and link as a DLL. 
  2099. o Add your resources to the DLL in the same manner as you would to an 
  2100.   executable. 
  2101.  
  2102. Then, when your application starts, you simply call WinLoadLibrary  (the 
  2103. preferred way) or DosLoadModule  to load the DLL.  These functions return a 
  2104. handle to the DLL which must then be used in any function which loads resources 
  2105. (e.g. GpiLoadBitmap, WinLoadPointer, etc.). 
  2106.  
  2107. Note that this procedure does not require knowing the window handle of any 
  2108. previous instance of your application because OS/2 implements DLLs in a shared 
  2109. fashion (which I suspect is one of the reasons they were created in the first 
  2110. place).  All you need to know is the name of the DLL.  This technique can also 
  2111. be used to share resources between different applications. 
  2112.  
  2113.  
  2114. ΓòÉΓòÉΓòÉ 5.1.1.3. Device independence ΓòÉΓòÉΓòÉ
  2115.  
  2116. A reader who desires to remain anonymous writes: 
  2117.  
  2118. Generally:  My understanding was that OS/2 would handle printing for me.  That 
  2119. is to say that I wouldn't have to create separate printer drivers for every 
  2120. printer under the sun (or any for that matter).  Since I am creating an image 
  2121. on the screen that is device independent (well, mostly anyway), is there an 
  2122. easy way to get printer output? 
  2123.  
  2124. PM achieves a level of device independence by defining a logical output space. 
  2125. This logical output space is then bound to a physical output space, which 
  2126. creates a mapping of logical characteristics to their physical counterparts. 
  2127. The logical and physical output spaces are referred to as the presentation 
  2128. space and the device context (HPS and HDC) and are bound to one another by 
  2129. using either the GpiAssociate function or by specifying GPIA_ASSOC to the 
  2130. GpiCreatePS function. 
  2131.  
  2132. The easiest way to accomplish what you desire is to organize your drawing code 
  2133. into one or more functions with a single entrypoint that accepts an HPS as a 
  2134. parameter.  Then, when you want to draw to the screen, you can call 
  2135. WinGetPS/WinBeginPaint to get an HPS and call the function. When you want 
  2136. hardcopy, you call DevOpenDC to get an HDC and GpiCreatePS to get an HPS and 
  2137. call the function. 
  2138.  
  2139. Note that to get hardcopy, you need to perform some additional setup to get 
  2140. things to work properly.  The two most important things are that you initialize 
  2141. the DEVOPENSTRUC structure properly before calling DevOpenDC and that you send 
  2142. the following escape codes (via DevEscape) at the following times: 
  2143.  
  2144. hdcPrn=DevOpenDC(...);
  2145. hpsPrn=GpiCreatePS(...);
  2146.  
  2147. DevEscape(...,DEVESC_STARTDOC,...);
  2148.  
  2149. if (!doDraw(hpsPrn)) {
  2150.    DevEscape(...,DEVESC_ABORTDOC,...);
  2151. } /* endif */
  2152.  
  2153. DevEscape(...,DEVESC_ENDDOC,...);
  2154.  
  2155. GpiDestroyPS(hpsPrn);
  2156. DevCloseDC(hdcPrn);
  2157.  
  2158. I'm not sure because I can't seem to find my copy anywhere, but I belive that 
  2159. the book by Graham Winn (entitled something to the effect of "Building 
  2160. applications using the OS/2 Presentation Manager") dedicates a chapter to the 
  2161. nuances of printing. 
  2162.  
  2163.  
  2164. ΓòÉΓòÉΓòÉ 5.2. Introduction to PM ΓòÉΓòÉΓòÉ
  2165.  
  2166.                                  Introduction to
  2167.                                  PM Programming
  2168.  
  2169.                                      Part I
  2170.  
  2171.  
  2172. ΓòÉΓòÉΓòÉ 5.2.1. Overview ΓòÉΓòÉΓòÉ
  2173.  
  2174.   Overview 
  2175.  
  2176. It can be quite a daunting task to sit down and learn to write Presentation 
  2177. Manager programs. There is so much ground to cover, but fear not - getting over 
  2178. the initial learning curve is the hardest part.  Once you have a grasp of the 
  2179. basics, it is fairly easy to pick up on more advanced techniques. 
  2180.  
  2181. Here I intend to provide a starting point for C programmers to get into PM, so 
  2182. that you do have a solid grounding.  Now unfortunately there are not many books 
  2183. on PM which explain not just the what, but the why.  That is, not just 
  2184. presenting a syntax diagram and saying that (for example) WinBeginPaint is used 
  2185. when you want to paint a window, but why you need it, when to use it (and not 
  2186. WinGetPS ), and how to use it. 
  2187.  
  2188. I will endeavour to explain the why, so you understand exactly what you are 
  2189. doing, instead of blindly copying code from examples.  You will see the code, 
  2190. and at each step we will discuss the important sections, so you can see just 
  2191. what each API does.  You certainly don't have to remember every single API 
  2192. though (there are hundreds). As you have a need to do something, you will learn 
  2193. the new APIs required to achieve that. 
  2194.  
  2195. Please remember that, as this is an introductory article, it is not presented 
  2196. as being 100% technically correct and accurate. It is more to give the reader a 
  2197. practical feel for what is going on. Technical references for technical 
  2198. questions appear in the Bibliography. And remember, you can always read the 
  2199. headers! 
  2200.  
  2201.  
  2202. ΓòÉΓòÉΓòÉ 5.2.2. Scope ΓòÉΓòÉΓòÉ
  2203.  
  2204.   Scope 
  2205.  
  2206. I am assuming that you are a competent C programmer, and have a working 
  2207. knowledge of OS/2 from a user's perspective. The sample code here was produced 
  2208. with IBM's C Set/2, and is ANSI C. I do not assume anything but a familiarity 
  2209. with GUIs in general. 
  2210.  
  2211.  
  2212. ΓòÉΓòÉΓòÉ 5.2.3. Trademarks etc. ΓòÉΓòÉΓòÉ
  2213.  
  2214.   Trademarks etc. 
  2215.  
  2216. Please note that any trademarks referred to in this article remain the property 
  2217. of their respective companies. 
  2218.  
  2219. #include <std_disclaimer.h>
  2220.  
  2221.  
  2222. ΓòÉΓòÉΓòÉ 5.2.4. Presentation Manager ΓòÉΓòÉΓòÉ
  2223.  
  2224.   Presentation Manager 
  2225.  
  2226. Presentation Manager is the GUI which sits atop OS/2. It consists mainly of a 
  2227. series of DLLs  which contain all the controls, APIs  and other stuff which 
  2228. makes PM tick. The WPS is in effect a PM application itself, albeit with 
  2229. certain other special properties.  One good thing about PM is that because the 
  2230. bulk of the code to handle the user interface is in DLLs, applications tend to 
  2231. be smaller, as they contain largely just the code to handle the processing of 
  2232. the information. This lets the programmer focus on the "guts" of the program, 
  2233. rather than being concerned with the user interface. Also, PM provides a 
  2234. consistent and intuitive interface for the user along standard design 
  2235. guidelines. Of course, as you will no doubt see, there is still a great deal to 
  2236. learn about PM! 
  2237.  
  2238.  
  2239. ΓòÉΓòÉΓòÉ 5.2.4.1. The Event Driven Model ΓòÉΓòÉΓòÉ
  2240.  
  2241.   The Event Driven Model 
  2242.  
  2243. GUIs are modelled on an Event Driven paradigm. This is better understood when 
  2244. contrasted with the sequential nature of conventional programs. Imagine the 
  2245. following ficticious code which might appear in the menu of a program: 
  2246.  
  2247. DisplayMenu();
  2248.  
  2249. while (!kbhit())
  2250.     UpdateTime();
  2251.  
  2252. c=getch();
  2253.  
  2254. if (c==0x27)
  2255.     Quit();
  2256.  
  2257. You can see that in the while loop, the program is constantly polling the 
  2258. keyboard to check if a key has been pressed. This is a very inefficient 
  2259. practice, as the machine spends most of its time in that loop, doing nothing. 
  2260. In a multitasking environment, this waste of CPU time reduces system 
  2261. throughput, as the CPU could be doing something more useful. 
  2262.  
  2263. In OS/2 however, programs do not poll for input (whether from the mouse or 
  2264. keyboard). All input is managed by PM itself. This means that when the user 
  2265. clicks the mouse on a button for example, PM handles this event.  It puts the 
  2266. event into a queue for that application, and the function which handles this 
  2267. event (the window procedure) only gets called when it has a message waiting for 
  2268. it. As a result, the program only acts when it needs to, rather than constantly 
  2269. checking if it actually has something to do. 
  2270.  
  2271. So rather than following sequentially through the program, PM programs send, 
  2272. receive and respond to events. This technique requires a shift in thinking, but 
  2273. with practice it will become clear. It is arguably a more intuitive model for 
  2274. programming, and it also promotes good technique. 
  2275.  
  2276.  
  2277. ΓòÉΓòÉΓòÉ 5.2.4.1.1. Windows Everywhere ΓòÉΓòÉΓòÉ
  2278.  
  2279.   Windows Everywhere 
  2280.  
  2281. A Window is simply a rectangular area on the screen. A window can have certain 
  2282. properties or flags, and PM is full of them. They may or may not have a frame 
  2283. or border, a titlebar, or a menu. 
  2284.  
  2285. You may only think of a window as being the Frame window of an application. 
  2286. However buttons are windows too. So are dialog boxes, list boxes, scrollbars - 
  2287. in fact each control in PM is a window. We will explore this further in a 
  2288. future article, but for now just take it that a window is rectangle. 
  2289.  
  2290. Don't worry too much if this sounds a bit vague - it will become clear once you 
  2291. start making your own windows. 
  2292.  
  2293.  
  2294. ΓòÉΓòÉΓòÉ 5.2.4.1.2. Getting the Message ΓòÉΓòÉΓòÉ
  2295.  
  2296.   Getting the Message 
  2297.  
  2298. The messages discussed in our talk of Events is simply a C struct, which looks 
  2299. like this: 
  2300.  
  2301. typedef struct _QMSG    /* Queue Message        */
  2302. {
  2303.    HWND    hwnd;
  2304.    ULONG   msg;
  2305.    MPARAM  mp1;
  2306.    MPARAM  mp2;
  2307.    ULONG   time;
  2308.    POINTL  ptl;
  2309.    ULONG   reserved;
  2310. } QMSG;
  2311.  
  2312. This struct is a key element in PM. The fields are explained thus: 
  2313.  
  2314. Γûá  hwnd - Window Handle - unique reference for a given window. 
  2315.  
  2316. Γûá  msg - A system- or user-defined message 
  2317.  
  2318. Γûá  mp1 and mp2 - Message parameters (meaning dependent upon the message). Can 
  2319. be a number, a pointer, a handle, etc. 
  2320.  
  2321. Γûá  time - The time stamp when the event ocurred. 
  2322.  
  2323. Γûá  ptl - The (x,y) co-ordinates of the mouse. 
  2324.  
  2325. Γûá  reserved - Just what it says! 
  2326.  
  2327. All messages have a unique number, and the system messages are all #defined in 
  2328. the OS/2 headers. Some typical messages include: 
  2329.  
  2330. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2331. ΓöéMessage   ΓöéDescription                             Γöé
  2332. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2333. ΓöéWM_CHAR   ΓöéWhen the user presses a key             Γöé
  2334. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2335. ΓöéWM_CLOSE  Γöéif the user chooses to Exit the program Γöé
  2336. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2337. ΓöéWM_PAINT  Γöéwhen an area of the screen needs to be  Γöé
  2338. Γöé          Γöédrawn                                   Γöé
  2339. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2340.  
  2341. You will not normally deal with the QMSG structure directly - your window 
  2342. procedure will handle this, and deals only with the handle, the message and the 
  2343. two parameters (mp1 and mp2). 
  2344.  
  2345.  
  2346. ΓòÉΓòÉΓòÉ 5.2.4.1.3. Having a Handle on Things ΓòÉΓòÉΓòÉ
  2347.  
  2348.   Having a Handle on Things 
  2349.  
  2350. A handle is a unique number or reference for any given system object. The most 
  2351. common handles refer to windows. An application can have any number of windows, 
  2352. and handles are a convenient way to keep track of them. Handles are also used 
  2353. internally by PM to manage its own information on the system. 
  2354.  
  2355.  
  2356. ΓòÉΓòÉΓòÉ 5.2.4.1.4. Window Procedures ΓòÉΓòÉΓòÉ
  2357.  
  2358.   Window Procedures 
  2359.  
  2360. A Window Procedure is one of the most important parts of any PM application. 
  2361. Its job is to handle all of the messages that get sent to its window. It 
  2362. usually consists of a big switch statement, with a case for each message. You 
  2363. can handle as many or as few messages as you wish, because (fortunately!) one 
  2364. of the APIs in PM is a default window procedure. So any message you don't need 
  2365. or want to handle, you pass on to WinDefWindowProc, which handles it in the 
  2366. default manner. 
  2367.  
  2368. Well, enough of this banter - let's do something useful and make a PM program. 
  2369.  
  2370.  
  2371. ΓòÉΓòÉΓòÉ 5.2.5. Your first PM Program ΓòÉΓòÉΓòÉ
  2372.  
  2373.   Your first PM Program 
  2374.  
  2375. Let's start simply by getting a plain window on screen. I will show you the 
  2376. code, followed by explanations, with the full source at the end of this 
  2377. section. 
  2378.  
  2379. /* Definitions */
  2380.  
  2381. #define INCL_WINFRAMEMGR
  2382.  
  2383. #define MYAPP           "MyApp"
  2384. #define ID_MYWINDOW     42
  2385.  
  2386. If you look at the file OS2.H, you will see a whole bunch of #ifdefs, and 
  2387. #includes. Because the header files are so large and numerous, and you don't 
  2388. always want to include everything since you may not use all of it, all you need 
  2389. do is #define which parts you need to include. There are certain things which 
  2390. are included by default, so here we only neet to define INCL_WINFRAMEMGR, for 
  2391. the definition of WM_ERASEBACKGROUND, as all the rest is already there. The 
  2392. most important header is PMWIN.H, so take some time to browse through it as it 
  2393. has some very important things in it. 
  2394.  
  2395. /* Includes */
  2396. #include <os2.h>
  2397.  
  2398. Having defined which sections we need from the headers, all we need at the 
  2399. moment is to include OS2.H. 
  2400.  
  2401. /* Prototypes */
  2402.  
  2403. MRESULT EXPENTRY MyWinProc (HWND, ULONG, MPARAM, MPARAM);
  2404.  
  2405. We prototype our functions, like all good ANSI programmers! 
  2406.  
  2407. /* Global Variables */
  2408.  
  2409. HAB     hab;
  2410. HWND    hwndClient,
  2411.         hwndFrame;
  2412.  
  2413. The HAB stands for a Handle to the Anchor Block. As you know, a handle is just 
  2414. a reference, but the anchor block is an internal data structure which PM 
  2415. manages. The origin of the name is a little obscure. Anyway, each instance of 
  2416. an application has an HAB. There are few times when you actually use it, 
  2417. usually only during the initialization of your program. For now, let's say it 
  2418. is like a handle to your application. 
  2419.  
  2420. Now we have two other handles, this time handles to windows. Notice that we 
  2421. have two handles. One for the client, one for the frame. This requires a little 
  2422. more detail. 
  2423.  
  2424. The Frame Window is the window from the edges of the resizeable frame border. 
  2425. It contains the border, titlebar, minimize and maximize buttons, system menu, 
  2426. and optionally a menu. 
  2427.  
  2428. The Client Window is the area of whitespace bordered by the frame and the menu. 
  2429. This is the space where information is displayed, and the user interacts with. 
  2430. Take a look at the System Editor for example. The frame window encompasses the 
  2431. entire application window. The client area is the area of white where the text 
  2432. appears (the edit window). Having a separate client window makes it easier to 
  2433. paint, scroll, and other such operations. 
  2434.  
  2435. /* main function */
  2436.  
  2437. void main (void)
  2438. {
  2439.     HMQ     hmq;
  2440.     QMSG    qmsg;
  2441.     ULONG   flCreate;
  2442.  
  2443.     hab = WinInitialize (0);
  2444.     if (!hab)
  2445.         return;
  2446.  
  2447. Here we launch straight into the main function. First we declare some local 
  2448. variables. The HMQ is a handle to a message queue. When an application is 
  2449. created, it needs a queue to keep the pending messages in, as they await 
  2450. processing by the window procedure. 
  2451.  
  2452. The QMSG is the data structure we looked at earlier (see Messages). This is 
  2453. used in the main message loop below. The flCreate is used to keep the flags we 
  2454. need to create our window. 
  2455.  
  2456. The very first thing we do is call WinInitialize. This initializes certain 
  2457. things in PM and gives us an HAB. If we could not get a HAB, something really 
  2458. bad has gone wrong, so we exit immediately. 
  2459.  
  2460.     hmq = WinCreateMsgQueue (hab, 0);
  2461.  
  2462. Here we create our message queue with our HAB, and get a handle to it so we can 
  2463. refer to it later. 
  2464.  
  2465.     WinRegisterClass(
  2466.         hab,
  2467.         MYAPP,
  2468.         (PFNWP) MyWinProc,
  2469.         CS_SIZEREDRAW,
  2470.         0);
  2471.  
  2472. Because each application behaves differently and has their own window 
  2473. procedure, PM needs to know certain things about your application. This is 
  2474. called registering. You give it your HAB, a unique string (called the class 
  2475. name) which identifies your program, a pointer to your window procedure (which 
  2476. is why you cast it) and any particular flags you may require. These flags are 
  2477. called Class Styles, and CS_SIZEREDRAW means we want our application to be 
  2478. redrawn if it is resized. The last parameter allows us to reserve some space 
  2479. within each window for instance-specific data, but we don't need this yet. 
  2480.  
  2481.  
  2482. ΓòÉΓòÉΓòÉ 5.2.5.1. Creating the Window ΓòÉΓòÉΓòÉ
  2483.  
  2484.   Creating the Window 
  2485.  
  2486.     flCreate =  FCF_TITLEBAR    | FCF_SYSMENU       |
  2487.                 FCF_SIZEBORDER  | FCF_MINMAX        |
  2488.                 FCF_TASKLIST    | FCF_SHELLPOSITION ;
  2489.  
  2490.     hwndFrame = WinCreateStdWindow(
  2491.         HWND_DESKTOP,
  2492.         WS_VISIBLE,
  2493.         &flCreate,
  2494.         MYAPP,
  2495.         "Sample PM App",
  2496.         0L,
  2497.         NULLHANDLE,
  2498.         ID_MYWINDOW,
  2499.         &hwndClient);
  2500.  
  2501. First we set up a variable which has ORed together a bunch of flags (called 
  2502. Frame Control Flags) to tell PM what sort of window we want. They mean (in 
  2503. order) we want a titlebar, a system menu, a resizeable border, both minimize 
  2504. and maximize buttons, we want it to appear in the task list, and we don't care 
  2505. what it's initial size or position is (let the shell decide). 
  2506.  
  2507. Now comes the actual window creation. We specify the parent window (usually the 
  2508. desktop, as is here). WS_VISIBLE means we want it to be visible, pass the frame 
  2509. control flags, our class name (so it knows which window procedure to use), the 
  2510. title of the window, then any other special flags. NULLHANDLE tells it to look 
  2511. for Resources (such as icons, menus, dialogs, etc) in the executable itself. We 
  2512. pass the ID of the window (to distinguish it from other windows, and to 
  2513. associate resources with), and finally get a handle to our client window. I 
  2514. hope you're keeping up so far, because we're almost there! 
  2515.  
  2516.     while (WinGetMsg (hab,&qmsg,(HWND)NULL,0,0))
  2517.         WinDispatchMsg (hab,&qmsg);
  2518.  
  2519. This is the most crucial part of the program, and is really what gives it the 
  2520. capability to respond to events. It is a constant loop which gets a message 
  2521. from the queue and dispatches it to the window procedure. WinGetMsg will keep 
  2522. going until it receives the WM_QUIT message. WinDispatchMsg actually gets PM to 
  2523. call your window procedure, which is why all window procedures have the same 
  2524. parameters (hwnd, msg, mp1, mp2). The other parameters in WinGetMsg allow you 
  2525. to do fancy things which we won't get into now. 
  2526.  
  2527.     WinDestroyWindow(hwndFrame);
  2528.     WinDestroyMsgQueue(hmq);
  2529.     WinTerminate(hab);
  2530. }
  2531.  
  2532. This is just for cleanup - once the user exits the application, we clean up and 
  2533. release the resources we used. 
  2534.  
  2535. /* our window procedure */
  2536.  
  2537. MRESULT EXPENTRY MyWinProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  2538. {
  2539.  
  2540. As I mentioned above, because PM calls your window procedure, you must have it 
  2541. declared exactly like this. EXPENTRY tells the compiler that this function will 
  2542. be in the list of exports (a list of " public" functions). 
  2543.  
  2544.     switch (msg)
  2545.     {
  2546.         case WM_ERASEBACKGROUND:
  2547.             return (MRESULT) TRUE;
  2548.             break;
  2549.  
  2550. Here we begin our amazing switch statement, which branches on the message. For 
  2551. each case, we have a Window Message. This first one is PM asking if we want it 
  2552. to erase the background for us. If we return true, it will do so. If we want to 
  2553. paint the background (or client area) ourselves, we would return False. 
  2554.  
  2555.         case WM_CLOSE:
  2556.             if (WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,
  2557.                               "Are you sure you want to quit?","Sample",
  2558.                               0,MB_YESNO | MB_QUERY) == MBID_YES)
  2559.                 WinPostMsg(hwnd,WM_QUIT,0L,0L);
  2560.             break;
  2561.  
  2562. This case is somewhat special. WM_CLOSE is sent when the user presses Alt-F4, 
  2563. selects Close from the System menu, double clicks on the System menu, selects 
  2564. Close from the Window List, or selects File|Exit from the menu (if there is 
  2565. one). This gives us a chance to ask the user if they are sure they want to 
  2566. quit, and make sure there are no changes to any files they might wish to save. 
  2567. Here we use the WinMessageBox API, passing it the parent and owner, the message 
  2568. we wish to display, the title of the message box, an ID (used for providing 
  2569. Help), and some flags. The flags specify that we want Yes and No buttons, and a 
  2570. Query icon. The call will return MBID_YES if they pressed the Yes button, in 
  2571. which case we send our application the WM_QUIT message. This causes WinGetMsg 
  2572. (in main) to return FALSE, the while loop to terminate, and finally cleanup 
  2573. before exiting. 
  2574.  
  2575.         default:
  2576.             return WinDefWindowProc (hwnd, msg, mp1, mp2);
  2577.     }
  2578.     return FALSE;
  2579. }
  2580.  
  2581. This part makes life easy for us. There are a myriad of other messages that our 
  2582. application receives, and WinDefWindowProc will handle them all for us. This is 
  2583. part of the beauty of the event-driven model - we grab only the messages which 
  2584. interest us, and let PM do the rest. This gives us a standard look and feel, 
  2585. with no extra work. 
  2586.  
  2587. Now we have written a bare-bones PM application, follow the instructions in the 
  2588. next section to run it. 
  2589.  
  2590.  
  2591. ΓòÉΓòÉΓòÉ 5.2.5.2. Instructions ΓòÉΓòÉΓòÉ
  2592.  
  2593.   Instructions 
  2594.  
  2595. You can extract the source code to a file. To do this, for each file, go to the 
  2596. code, press Ctrl-F. This will create a file called TEXT.TMP in the root 
  2597. directory of the current drive. Go to an OS/2 Window and copy that file to a 
  2598. working directory, renaming it to the respective name (which appears in the 
  2599. title). You should be able to compile and run the program straight away. Just 
  2600. issue the command: 
  2601.  
  2602. [C:\WORK\PM]nmake /f step01.mak
  2603.  
  2604. And then run: 
  2605.  
  2606. [C:\WORK\PM]step01
  2607.  
  2608. You should see a plain window with a titlebar. Closing the window will confirm 
  2609. with a dialog. Once you have seen the program running, go back and re-read the 
  2610. section explaining the code, so you can see just what is happening. 
  2611.  
  2612.  
  2613. ΓòÉΓòÉΓòÉ 5.2.6. Sample Code ΓòÉΓòÉΓòÉ
  2614.  
  2615.   Sample Code 
  2616.  
  2617. [The code presented in this article is also available in 'intropm.zip'. -Ed.] 
  2618.  
  2619. Γûá  STEP01.C 
  2620.  
  2621. Γûá  STEP01.MAK 
  2622.  
  2623.  
  2624. ΓòÉΓòÉΓòÉ 5.2.6.1. STEP01.C ΓòÉΓòÉΓòÉ
  2625.  
  2626. /* Definitions */
  2627.  
  2628. #define INCL_WINFRAMEMGR
  2629.  
  2630. #define MYAPP           "MyApp"
  2631. #define ID_MYWINDOW     42
  2632.  
  2633. /* Includes */
  2634.  
  2635. #include <os2.h>
  2636.  
  2637. /* Prototypes */
  2638.  
  2639. MRESULT EXPENTRY MyWinProc (HWND, ULONG, MPARAM, MPARAM);
  2640.  
  2641. /* Global Variables */
  2642.  
  2643. HAB     hab;
  2644. HWND    hwndClient,
  2645.         hwndFrame;
  2646.  
  2647. /* main function */
  2648.  
  2649. void main (void)
  2650. {
  2651.     HMQ     hmq;
  2652.     QMSG    qmsg;
  2653.     ULONG   flCreate;
  2654.  
  2655.     hab = WinInitialize (0);
  2656.     if (!hab)
  2657.         return;
  2658.  
  2659.     hmq = WinCreateMsgQueue (hab, 0);
  2660.     WinRegisterClass(
  2661.         hab,
  2662.         MYAPP,
  2663.         (PFNWP) MyWinProc,
  2664.         CS_SIZEREDRAW,
  2665.         0);
  2666.  
  2667.     flCreate =  FCF_TITLEBAR    | FCF_SYSMENU       |
  2668.                 FCF_SIZEBORDER  | FCF_MINMAX        |
  2669.                 FCF_TASKLIST    | FCF_SHELLPOSITION ;
  2670.  
  2671.     hwndFrame = WinCreateStdWindow(
  2672.         HWND_DESKTOP,
  2673.         WS_VISIBLE,
  2674.         &flCreate,
  2675.         MYAPP,
  2676.         "Sample PM App",
  2677.         0L,
  2678.         NULLHANDLE,
  2679.         ID_MYWINDOW,
  2680.         &hwndClient);
  2681.  
  2682.     while (WinGetMsg (hab,&qmsg,(HWND)NULL,0,0))
  2683.         WinDispatchMsg (hab,&qmsg);
  2684.  
  2685.     WinDestroyWindow(hwndFrame);
  2686.     WinDestroyMsgQueue(hmq);
  2687.     WinTerminate(hab);
  2688. }
  2689.  
  2690. /* our window procedure */
  2691.  
  2692. MRESULT EXPENTRY MyWinProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  2693. {
  2694.     switch (msg)
  2695.     {
  2696.         case WM_ERASEBACKGROUND:
  2697.             return (MRESULT) TRUE;
  2698.             break;
  2699.  
  2700.         case WM_CLOSE:
  2701.             if (WinMessageBox(HWND_DESKTOP,HWND_DESKTOP,
  2702.                               "Are you sure you want to quit?","Sample",
  2703.                               0,MB_YESNO | MB_QUERY) == MBID_YES)
  2704.                 WinPostMsg(hwnd,WM_QUIT,0L,0L);
  2705.         break;
  2706.  
  2707.         default:
  2708.             return WinDefWindowProc (hwnd, msg, mp1, mp2);
  2709.     }
  2710.     return FALSE;
  2711. }
  2712.  
  2713.  
  2714. ΓòÉΓòÉΓòÉ 5.2.6.2. STEP01.MAK ΓòÉΓòÉΓòÉ
  2715.  
  2716. #--------------------------------------------------------------------
  2717. #
  2718. # Makefile for STEP01.MAK
  2719. #
  2720. #--------------------------------------------------------------------
  2721.  
  2722. CC          = icc
  2723. LINK        = link386
  2724. CFLAGS      = /c /Ti
  2725. LFLAGS      = /pmtype:pm /debug
  2726.  
  2727. ┬╖c.obj:
  2728.    $(CC) $(CFLAGS) $*.c
  2729.  
  2730. all: step01.exe
  2731.  
  2732. step01.exe: step01.obj
  2733.    $(LINK) $(LFLAGS) step01,,,os2386,;
  2734.  
  2735.  
  2736. ΓòÉΓòÉΓòÉ 5.2.7. What Next? ΓòÉΓòÉΓòÉ
  2737.  
  2738.   What Next? 
  2739.  
  2740. Now that you have a basic PM application running, in the next issue we will add 
  2741. a menu, some dialogs, and respond to some messages that do something useful. It 
  2742. may seem like a lot of code to just get a plain window to appear, but we will 
  2743. see how easy it is to add features next month. 
  2744.  
  2745. I welcome any feedback on this article (netmail preferred) - any comments or 
  2746. suggestions you may have, questions on this article, or things you would like 
  2747. to see in a future article. I hope you have learned something! 
  2748.  
  2749.  
  2750. ΓòÉΓòÉΓòÉ 5.2.8. Bibliography ΓòÉΓòÉΓòÉ
  2751.  
  2752.   Bibliography 
  2753.  
  2754. The following references were used in the preparation of this article: 
  2755.  
  2756. Γûá  OS/2 Version 2.0 - The Redbooks 
  2757.  
  2758. Γûá  OS/2 Version 2.0 Technical Library 
  2759.  
  2760.  
  2761. ΓòÉΓòÉΓòÉ 5.3. Project Barrel ΓòÉΓòÉΓòÉ
  2762.  
  2763.                                  Project Barrel
  2764.  
  2765.  
  2766. ΓòÉΓòÉΓòÉ 5.3.1. Project Ideas ΓòÉΓòÉΓòÉ
  2767.  
  2768. This column is a place where I intend to throw out ideas that I have come 
  2769. across, either by thinking of them myself or by listening around on the net. 
  2770. Some of these ideas are more practical than others; all of them should be worth 
  2771. at least thinking about. 
  2772.  
  2773. Feel free to attack anything you see here; similarly, feel free to send in your 
  2774. ideas, in as much or as little detail as you wish. 
  2775.  
  2776. Icon Management - A commercial application is available that does most of this 
  2777. now. However, Windows users typically don't have to pay for this level of 
  2778. utility and there's no reason we should either. Ideally, the program would 
  2779. allows for easy movement into and out of zipfiles (or a similar compressed 
  2780. storage archive) and allow for drag 'n drop attachment to programs. The ability 
  2781. to view more than the 100 or so the drives object can view would also be a 
  2782. plus. 
  2783.  
  2784. Network software - Let's face it, the software that comes with TCP/IP 1.2.1 is 
  2785. crap. Telnet only runs in 25 line mode, LaMail is a hideous ordeal to set up, 
  2786. RN doesn't even work. There's a lot of opportunity here to write one of those 
  2787. apps that people use every day of their lives. 
  2788.  
  2789. Games - OS/2 Klondike is nice, but how about something that really shows off 
  2790. what a 32 bit multithreaded program can do? For Presentation Manager, StarTrek 
  2791. springs to mind as a possibility, as does an RPG a'la Castle of the Winds. In 
  2792. fullscreen mode anything is possible. I would also like to see some multiplayer 
  2793. network games - I just saw a friend playing Bolo on the Mac and didn't see any 
  2794. reason why I shouldn't have something like it on my machine. 
  2795.  
  2796. Emacs - Yes, I actually like the beast (I did this entire magazine with Emacs). 
  2797. What I would like (and I have a vested interest in this) is an emacs major mode 
  2798. for editing .IPF files. One that would match tags (in the same way that c-mode 
  2799. matches parentheses) and provide for a consistent style with headers, comments, 
  2800. etc. 
  2801.  
  2802. Etc. - Ever look and see how HUGE the Windows areas are on most BBS's? There's 
  2803. no reason why there shouldn't be that much stuff out there for OS/2 - the two 
  2804. systems are similar in programming style and there's even an entire C compiler 
  2805. for OS/2 free for the asking. Anytime you see a Windows program you like, 
  2806. figure out what it is you like and port it! 
  2807.  
  2808.  
  2809. ΓòÉΓòÉΓòÉ 6. Future Attractions ΓòÉΓòÉΓòÉ
  2810.  
  2811. Coming up in the future, we have: 
  2812.  
  2813. o Introduction to PM Part 2 
  2814.  
  2815. o Writing Installable File Systems 
  2816.  
  2817. o Getting Started with IPF 
  2818.  
  2819. o And much more! 
  2820.  
  2821.  
  2822. ΓòÉΓòÉΓòÉ 7. Contributors to this issue ΓòÉΓòÉΓòÉ
  2823.  
  2824. o Steve Luzynski 
  2825.  
  2826. o David Charlap 
  2827.  
  2828. o Larry Salomon 
  2829.  
  2830. o Raja Thiagarajan 
  2831.  
  2832. o Gavin R Baker 
  2833.  
  2834. o Steve Lacy 
  2835.  
  2836.  
  2837. ΓòÉΓòÉΓòÉ 7.1. Steve Luzynski ΓòÉΓòÉΓòÉ
  2838.  
  2839. Steve Luzynski is the editor and creator of this magazine. He is currently a 
  2840. Computer Engineering student at Case Western Reserve University in Cleveland, 
  2841. OH, where he spends a lot of time being cold. Steve has yet to release any 
  2842. programs for OS/2 as a direct result of: 1) editing this magazine; and 2) 
  2843. having to waste time going to class when there are programs to write. Steve can 
  2844. by reached via e-mail at 'sal8@po.cwru.edu' or on Compuserve at 72677,2140. 
  2845.  
  2846.  
  2847. ΓòÉΓòÉΓòÉ 7.2. David Charlap ΓòÉΓòÉΓòÉ
  2848.  
  2849. David Charlap is 23 years old and is a full-time student at New Jersey 
  2850. Institute Of Technology.  He is pursuing a Masters degree in computer science. 
  2851. He learned OS/2 programming while working for a software development company 
  2852. and is currently writing small OS/2 applications for shareware consumption. 
  2853. MineSweeper is his first application that has been released to the general 
  2854. public.  David may be reached by e-mail at: 
  2855.  
  2856. dic5340@hertz.njit.edu 
  2857.  
  2858. He may also be reached at the following address: 
  2859.  
  2860.    David Charlap
  2861.    8 Snow Ridge
  2862.    Denville, NJ 07834
  2863.    USA
  2864.  
  2865.  
  2866. ΓòÉΓòÉΓòÉ 7.3. Larry Salomon ΓòÉΓòÉΓòÉ
  2867.  
  2868. Larry Salomon wrote his first Presentation Manager application for OS/2 version 
  2869. 1.1 in 1989.  Since that time, he has written numerous VIO and PM applications, 
  2870. including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen 
  2871. Capture trio included with the IBM Professional Developers Kit CD-ROM currently 
  2872. being distributed by IBM.  Currently, he works for International Masters 
  2873. Publishers in Stamford, Connecticut and resides in Bellerose, New York with his 
  2874. wife Lisa. 
  2875.  
  2876.  
  2877. ΓòÉΓòÉΓòÉ 7.4. Raja Thiagarajan ΓòÉΓòÉΓòÉ
  2878.  
  2879. Raja Thiagarajan was born in Madras, India in 1965, but moved to Bloomington, 
  2880. Indiana by the age of two in order to get a 25-year headstart on writing "An 
  2881. Unofficial Guide to the Palette Manager." Raja has college degrees in 
  2882. Astrophysics and Computer Science, but will never understand NASA's priorities, 
  2883. the C preprocessor, Scheme's call/cc, or Prolog's cut operator. Raja would love 
  2884. to hear from you; send e-mail to sthiagar@bronze.ucs.indiana.edu 
  2885.  
  2886.  
  2887. ΓòÉΓòÉΓòÉ 7.5. Gavin R Baker ΓòÉΓòÉΓòÉ
  2888.  
  2889.   Gavin R Baker 
  2890.  
  2891. Gavin R Baker is the man behind  ThinkSoft, a consulting firm based in 
  2892. Melbourne Australia which specialises in developing custom software. He has 
  2893. experience in Assembler, Pascal, C, C++, (and a lot of other languages), and 
  2894. has worked with Unix, DOS, Windows, OS/2, VMS and Pick operating systems. He is 
  2895. an active member of Team OS/2. When he isn't programming, he is also a 
  2896. musician, an actor, and wastes lots of time reading Net News. He can be 
  2897. contacted thusly: 
  2898.  
  2899. net: demogrb@lust.latrobe.edu.au
  2900. bix: gbaker
  2901. cis: 100026,270
  2902.  
  2903.  
  2904. ΓòÉΓòÉΓòÉ 7.6. Steve Lacy ΓòÉΓòÉΓòÉ
  2905.  
  2906. Steve Lacy is a third year computer science student at Carnegie Mellon 
  2907. University, and is currently overoccupied with school, and his job as an 
  2908. undergraduate research programmer for the Digital Mapping Lab at CMU.  You can 
  2909. reach Steve via e-mail at sl31@andrew.cmu.edu. He would be glad to hear any and 
  2910. all of your comments about the previous article.  Steve also spends his free 
  2911. time collecting CD's, and unfortunately spends far too much money in the 
  2912. process.  Remember, diversity is knowledge, and personal gratification 
  2913. overrides the need for food in many cases..... 
  2914.  
  2915.  
  2916. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  2917.  
  2918.   Graphical User Interface 
  2919.  
  2920. An interface usually comprising multiple overlapping windows, icons, pull-down 
  2921. menus, and the mouse as a pointing device. It is recognised that the GUI was 
  2922. invented by Xerox at PARC (Paolo Alto Research Centre) in the late 60's (??). 
  2923.  
  2924.  
  2925. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  2926.  
  2927.   Dynamic Link Library 
  2928.  
  2929. An executable module which can be shared between multiple applications, 
  2930. containing code and/or resources. It is loaded dynamically at run-time rather 
  2931. than being statically linked at compile time. This reduces memory requirements 
  2932. and allows code independance and code sharing. 
  2933.  
  2934.  
  2935. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  2936.  
  2937.   Multitasking 
  2938.  
  2939. Where the CPU shares itself between more than one task, by giving each task a 
  2940. slice of processing time. 
  2941.  
  2942.  
  2943. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  2944.  
  2945.   Workplace Shell 
  2946.  
  2947. The Workplace Shell is the Object Oriented shell for OS/2. 
  2948.  
  2949.  
  2950. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  2951.  
  2952.   Application Programming Interface 
  2953.  
  2954. API is a general term for a set of functions which provide a standard set of 
  2955. services to an application. In this article, we refer to the OS/2 APIs as the 
  2956. calls to OS/2 and PM which make up the system services in the kernel and the 
  2957. GUI. 
  2958.  
  2959.  
  2960. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  2961.  
  2962.   Parent 
  2963.  
  2964. The parent-child relationship establishes a hierarchy of windows in the system. 
  2965. Child windows are clipped to their parent windows; that is no child window will 
  2966. apear outside the boundary of its parent. 
  2967.  
  2968.  
  2969. ΓòÉΓòÉΓòÉ <hidden>  ΓòÉΓòÉΓòÉ
  2970.  
  2971.   Owner 
  2972.  
  2973. The owner of a window will receive messages from the windows it owns.