═══ 1. Aug 1995 Title Page ═══ The Electronic Developer's Magazine for OS/2 Portions copyright (c) by IQPac Inc. Volume 3, issue 7 Administrivia There has to be a section of Murphy's law's that deal with computers and specifically with making announcements. Immediately after announcing the EDM/2 Web site, the address changed and EDM/2 missed the next issue. The new URL is http://os2man.netsysdev.telerate.com . Ah, what three letters can do to a person. The Web site is growing nicely. A Gotcha section has been added which will grow into a repository of programming pitfalls, and it utilizes HTML forms to allow you to contribute directly to the page. Additionally, a weekly From the editor section was added to allow me to tell you about things that you should know as an OS/2 developer in a more timely fashion. Let's face it; if you read things once a month here, there's a good chance that the information is no longer valid by that time. Finally, a Feedback section was added to give you the opportunity to sound off; tell us what you think about anything. We'll be glad for your feedback. In any case, as we get more time, more things will get updated so keep a lookout and send us any feedback you have! Errata Last issue's article on IPC Between DOS Processes was incorrectly described as having been authored by Mike Ruddy. It was instead written by Leif N. Eriksen. Infoworld, Brian Livingston, et al. Those of you who are fortunate enough to read Infoworld on a regular basis probably know who Brian Livingston is - he is a "pro-Windows and everything else can go to hell" kind of a person - but in his July 24 column, he said something quite upsetting. Paraphrased, "if you are one of the 3 people who don't have a preview copy of Windows 95..." I've seen this attitude before and always make sure I send off some email to correct the situation; this time was no different. I told Brian that I've been using OS/2 since 1989 and have been quite happy with it. I have had no reason nor desire to go to Windows and probably never will. I selected the "Send" button and figured that was that. Boy was I wrong. His response was infuriating. Not only did he insult me by telling me that I should be saving his columns for the general availability of Windows 95, but he added "salt to the wound" by telling me in a post script that "the other two people also complained." In the words of Bugs Bunny, "this means war." My reply to him can be found in the From the editor section for the week beginning July 24. The only thing I request is, if you are like me and are happy with OS/2, that you drop him a line to tell him so. His email address is brian_livingston@infoworld.com . I am not encouraging you to flame him to a cinder. Simply let him know which operating system you will use on August 24. The amount of mail coming into his mailbox should help him to adjust his opinion. Stewart Alsop, on the other hand, always seems to come on strong in his columns. While I won't deny that he has a bite, he did surprise me also this past week. I have had email "discussions" (more like me venting a bit of frustration at him) with him and have always considered him to be another Windows bigot. I told him this in a recent email to which he responded, again paraphrasing, "that is an amusing concept." He enforced that with the claim that he has used a Mac since 1985, a fact I did not remember until after I read that. Well, at least I can't claim that he's so anti-IBM because he's so pro-Windows. Maybe there is a bit of objectivity in him after all... And Nicholas Petreley has to be out on a limb there. He's the only vocal pro-OS/2 writer there (the keyword is "vocal") and that has to be stressful. Help him out, people; if you know of some hot new product or development tool, let him know about it. He'll thank you for it, guaranteed. His address is nicholas_petreley@infoworld.com . It's That Time Again Gordon will, starting this issue, begin another round of C++ compiler comparisons, and he definitely has some interesting things to tell you. He plans to review two compilers per month, but that will depend on his schedule in future months. A related note is that, because this issue is quite large due to the screen shots included in the compiler review, the Introduction to PM Programming column will not be a part of this month's issue. ...And Speaking of Reviewing I have received a couple of tentative offers from people willing to try to be our Software Review columnist. If you are interested, simply let me know; the only commitment you have to keep is to review one development-related application per month. Spread the Word If you haven't visited the #os/2 channel on IRC yet, it's about time you did. There are IRC clients for most platforms and it isn't really that addicting . In #os/2, you can get your questions answered about configuration problems, user problems, and even programming problems. It's helpful for the "I need the answer now" problem. Some problems go unanswered but it's always worth a try, and the "regulars" are great fun. I'm always in there (with the nickname of Q--, unsurprisingly) and so are many other very knowledgeable people. Please Wait Another Month I made a promise in June to demonstrate how to make transparent windows, but this month has kept me quite busy, and I will continue to be so until August 15, when the ColoradOS/2 presentations are due to Wayne Kovsky. In fact, if I hadn't cancelled last month's issue, I would have done so this month. Please be patient; I will fulfill this promise next issue. One Final Note This is wrong, but I'm going to say it anyway: the 2nd edition of my book should hit the shelves in the U.S. on approximately September 1, according to John Wiley and Sons. It is entitled, The Art of OS/2 Warp Programming. If you want more information, stop by the "official" web site, also located at http://os2man.netsysdev.telerate.com. Enjoy this issue! ═══ 2. Copyright Notice ═══ Copyright Notice EDM/2 is published by IQPac Inc. IQPac Inc. can be reached via U.S. Mail at the following address: IQPac Inc. 7 East Broadway, Box 804 New York, NY 10038 U.S.A. Editor-in-chief Larry Salomon Jr. Associate editor Carsten Whimster Contributing editor Gordon Zeglinski CEO/President Larry Salomon Jr. All material is copyrighted by its original author. No part of this magazine may be reproduced without permission from the original author. This publication may be freely distributed in electronic form provided that all parts are present in their original unmodified form. A reasonable fee may be charged for the physical act of distribution; no fee may be charged for the publication itself. Neither IQPac Inc. nor this publication are affiliated with International Business Machines Corporation. OS/2 is a registered trademark of International Business Machines Corporation. Other trademarks are property of their respective owners. Any mention of a product in this publication does not constitute an endorsement or affiliation unless specifically stated in the text. The OS/2 Accredited Logo is a trademark of International Business Machines Corporation and is used by IQPac Inc. under license. This On-line Publication is independently produced by IQPac Inc. and IBM is not responsible in any way for its contents. IQPac Inc. is an accredited member of the IBM Independent Vendor League. Copyright Notice - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 3. Letters ═══ Letters To write to EDM/2, send your email to os2man@panix.com and use the Subject: line "Letters". 'Games' Was A Hit Lachlan O'Dea (drizzt@cc.monash.edu.au) writes: Thank you for your excelent Gearing Up For Games article in EDM/2. It was very interesting and easy to understand. I'm a good programmer, but I'm relatively new to OS/2 and I've never touched DIVE. I look forward to future articles by you on game programming in OS/2. EDM/2 keeps getting better and better! ──────────────────── ...So Was 'EXE Optimization' Steve Snyder (ssnyder@icon-stl.net) writes: Having just read your very good articles on EXE optimization ("Building Smaller OS/2 Executables", EDM issues 3-3 and 3-4), I have a question. It concerns the use of the LINK386 /BASE switch. You state that the use of /BASE:0x10000 eliminates the need for fixups, making the EXE smaller and the loading of it faster. I tried it on my program and in fact the 1.5MB EXE size shrunk by 80k! Now my question: Is there a downside to using this switch? If not, I don't understand why IBM did not make this the default. This looks so much like a free lunch that I'm looking for the hidden cost. Thanks for the informative articles. Pete Cassetta responds: Thanks for your nice comments about my articles. I was very glad to hear that your 1.5 MB EXE shrunk by 80K when you began using the /BASE:0x10000 linker switch! I suppose this switch does sound like something for nothing, but in this case you can have a free lunch. There is absolutely no downside to using this switch. If you benchmark your program carefully, you should see an improvement in load time as well. This switch simply causes the linker to resolve internal fixups at link time instead of leaving them in there to be resolved at load time. OS/2 allows this because its designers wisely decided that all EXEs would load at a base address of 0x10000. Hence the load address is always known at link time. The question of why LINK386 doesn't use /BASE:0x10000 by default for EXEs is a good one. I really don't know the answer, though I imagine there must be some historical or philisophical reason. You may be interested to know that the authors of C-Set++ agree with you that this switch should be on by default. If you compile and link by calling the ICC program that comes with C-Set++, it turns on the /BASE:0x10000 switch by default when calling LINK386. ICC actually turns on three size-related switches by default: /align:16 /exepack /base:65536 (taken from an ICC-generated command-line) Because of this, many programs built with C-Set++ get these switches turned on with no explicit effort on the part of their author. If you are using C-Set++, then I expect you wrote your own makefile which calls LINK386 directly. In this case you have to turn on the /BASE switch yourself. So don't fear the /BASE:0x10000 switch; it is a friend and should be used when linking all your EXE files. ──────────────────── A Request or Two Paul D. Walker (pdwalker@hk.super.net) writes: I have looked forward to the 'zine since I first discovered and read all the issues last year. I have enjoyed the publication as, I am sure, many others have. Since you did not receive any letters last month, I thought that I would at least break the silence and give you my support (whoopee!) Well, enough of that. What I would like to see is either a continuation of installable file systems (and the nuts and bolts of how to), or something on the internals (as far as anyone can find out) of HPFS. In general, I have found the third year of your publication (so far) to be the best yet. I especially enjoyed the articles on reducing code size, especially with todays programs getting bloated beyond everything imaginable (my simple clipper programs frequently go over 700KB nowadays because of the overhead of my library on top of clippers library.) Please keep up the good work. Please continue your introduction to PM programming (as one day soon I will go back and read all the articles and start programming under OS/2 myself...or wait for Visual Age (*grin*) and any other topic that you might be interested in yourself. If you are interested in a particular thing, then you can bet that someone else will be. (At least that is what an older more experienced DJ told me years ago when I was learning the trade...) Looking forward to the next issue... ──────────────────── Dirk Wissmann (wissmann@genesis.westend.com) writes: I absolutely appreciate your EDM/2 and I like the articles written there. Especially the "Introduction to PM Programming" is a favourite one, which I always read to increase my knowledge. But now I have reached the point where I have to use something, that was covered in no issue until now (including Vol. 3 Iss. 6): creation and handling of notebooks. When looking on the themes which were published in the section PM Programming, almost everything was important. And in my opinion, creating a notebook is as important as all the other themes - just think about making your PM application configurable. So how about this theme? Will this be something for an EDM/2 in the near future, or will it take a longer time until it will show up? Larry Salomon responds: As you might imagine, EDM/2 is dependent on the voluntary works of the contributors, so we cannot give a satisfactory answer since we do not know who will be submitting what. While I would love to get to the "meat" of things in the column, I want to avoid rushing the column since I felt that was a significant problem with the first effort at it. PM programming is quite difficult; rushing things would leave out significant details in all likelihood and might confuse the readers. Your feedback has been noted, however, and I will try to fish for someone to write an article on this topic. ──────────────────── Solving a Programming Problem Mark Mathews (mmathews@genesis.nred.ma.us) writes: I'm slowly porting DEU 5.3 (DOOM editor. DOOM? What's DOOM? ) over to OS/2. It's now drawing maps. I'm trying to figure out how to use mouse messages. I tried using WM_BUTTON1CLICK and WM_BUTTON2CLICK but if I try to double click the mouse I still activate the WM_BUTTON1CLICK. What I want to do is to click once to mark an object on the map or to click twice and edit the object. I don't have to mark an object to edit it. 'Mark' is used to select more than 1 item. So how do I distinguish between all these different mouse messages? Larry Salomon responds: There is no way to automatically do it, so you have to use some voodoo magic to get what you want. When you get the WM_BUTTON1CLICK message, call WinQuerySysValue(HWND_DESKTOP,SV_DBLCLKTIME) and start a timer (WinStartTimer) with the value returned. If you get a WM_BUTTON1DBLCLK message before you get the appropriate WM_TIMER message, then you process the "double click event" otherwise you process the "single click event". Don't forget to call WinStopTimer! ──────────────────── cdray@icis.on.ca writes: I've used just about every PM control (including containers) with great success, but it may be the lowly Button control which finally drives me insane. My particular problem is with the BS_BITMAP style. I have used the BS_ICON style with no problem. I am attempting to create a button, with bitmap, in the client area of my main window using WinCreateWindow. When I specify BS_ICON and the appropriate .ICO file, the button is displayed and functions correctly. When I specify BS_BITMAP and a .BMP file, nothing is displayed. Any help would be greatly appreciated! Larry Salomon responds: This was a bug in the 2.x toolkit. Are you using that or the Warp toolkit? I haven't verified the "fix" that I have hoped for in the latter. ──────────────────── Web Site Feedback Kevin Porter (kporter@nmia.com) writes: This may be beyond your control but an "expand all" button when reading the magazine would be very helpful, since there's a significant delay each time an expand is done. It's nice to find the stuff on-line, i've been off the distribution since my account moved a while ago and it's nice to see your work continuing/maturing. Hope it's going well for you. Keep up the good work! EDM/2 responds: Thanks for the compliments and comments. We are held hostage by Peter Childs, the author of the INF-HTML package until we get time to convert the issues to HTML format directly. ──────────────────── Mark Noble (mnoble@ukanvm.cc.ukans.edu) writes: The EDM/2 WWW setup is well done, however there's one thing that I'd like to see. I don't know what's involved in this, but if it isn't to difficult, is there any way you could "gather" all of the Q&A sections into one page? If the load on the server gets a little high, this might help. [Editor's note - Mark did this for us, but we have been too busy to incorporate it into the Web site.] ──────────────────── Documentation Error jrb@mfltd.co.uk writes: I received the info doc telling me how to subscribe to EDM/2 and where to find the back issues via FTP. I think the location is wrong and needs updating: It said hobbes.nmsu.edu /pub/os2/32bit/program/newsltr, but I found it in /os2/newsltr instead. EDM/2 responds: Was this doc obtained from the mail server (edm2-info@knex.mind.org) or from some other location? If the former, it should have been changed by now, so you might re- request it to check. If from some other location, I have no control over this, but you could send email to the site admin and tell them to get the document again. Thanks for pointing this out. Letters - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 4. Announcements ═══ Announcements Learn REXX with The REXX Cookbook and The Rexx Files Author: Merrill Callaway Publisher: Whitestone 511-A Girard Blvd SE Albuquerque, NM 87106 USA (505) 268-0678 email 5979987@mcimail.com Product Description and Ordering Information The REXX Cookbook is a unique tutorial designed to teach anyone, even non-programmers, to program in REXX by presenting detailed explanations of over 40 useful REXX programs. These programs and several more are on the companion disk THE REXX FILES. Those who have experienced the frustration of trying to write real world code after finding only fragmentary examples in reference books will really appreciate the programs in this book. Charles Daney of Quercus Systems and author of Programming in REXX, published by McGraw Hill, had this to say to Mr. Callaway after he reviewed it: "I was really impressed with the amount of work you put in on this. It's one of the most detailed computer books I've ever seen, and not just trivial detail either, but good solid information. And some of your examples are among the best I've seen anywhere. The stuff on PostScript, SQL, DB2/2, AmiPro, etc. should be really valuable to many." Callaway works from the premise that reference books on programming have too high a degree of abstraction and generalization to be tutorials, since they attempt to cover every case for every instruction. Learners must decipher templates and syntax diagrams rather than view explicit code in the context of a complete, concrete program. His book makes learning REXX far easier than ever before, because readers can see actual code used and explained in context. It is a bonus that the book's programs are so useful in their own right. There was as much software development as writing to produce this book. For instance one example project is an Inter-Process Control REXX program that combines VX-REXX by WATCOM, REXX and DB2/2 by IBM, and AmiPro by Lotus Development. Another unique program uses REXX and PostScript together to print envelopes quickly without needing to open a word processor. It even keeps a database of your personal addresses handy. There are some essential system utility programs to back up your .INI files, some EPM and TSPF editor macros, and even a program to teach you how to use the parse instruction. The level of difficulty is graduated so the book is suitable for beginners and experts alike. Everyone will find useful REXX code in The REXX Cookbook. Combined with any standard or online REXX reference, it rounds out a COMPLETE COURSE in REXX FLUENCY. Callaway says, "The REXX Cookbook is the very book I wished I'd had when I was learning to program in REXX!" The REXX Cookbook is OS/2 Accredited. How to order VISA/MasterCard orders may email or telephone with credit card number and expiration date. Checks or money orders (made payable to Whitestone) US Bank in US dollars are also acceptable (mail to publisher's address above). Price: Deluxe Set (book and disk): $42.90 (book ONLY: $27.95) Shipping and handling USA: $5.00 Shipping and handling elsewhere: $12.00 ──────────────────── Mindy Ported to OS/2 I have completed a port of Mindy 1.3 to OS/2. Mindy is an implementation of the Dylan language but Mindy is not Dylan yet. Dylan is a general-purpose Object Oriented Dynamic Language designed by Apple Computer. Quoting from Apple's Dylan Home Page at http://www.cambridge.apple.com/dylan/dylan.html: "Dylan is a new language developed at Apple. It is a bold new effort to create a powerful, practical tool for writing mainstream commercial applications. We believe it combines the best qualities of static languages (small, fast programs) with the best qualities of dynamic languages (rapid development, code that's easy to read, write and maintain)." Credits for Mindy, and therefore 99% of this port, go to the Gwydion Project at Carnegie Mellon University. Mindy is freely available and the modified source and executables have been uploaded to hobbes.nmsu.edu. The file is named mindy13.zip and it should be in the /os2/dev32/ directory. While you are there, get /os2/unix/emx09a/emxrt.zip if you don't have it. It contains DLLs required by the Mindy executables. hobbes.nmsu.edu is mirrored at ftp.cdrom.com in /pub/os2/. I only tested the port under OS/2 Warp. The Gwydion project released Mindy 1.3 but please send bug reports to me if you use this port. They have much better things to do than fix my bugs. ──────────────────── Introduction to OS/2 Programming in C Course From Distributed Systems Laboratories Education Services The five day course, Introduction to OS/2 Programming in C, is now available. DSL can schedule this course at your site, or provide materials under a license agreement. Rates are determined by volume and location (Country). All courses are taught in English, although translation services are available. The Intro to OS/2 Programming course will begin a complete OS/2 series of courses, including Advanced OS/2 Programming, Intro to PM Programming, and Advanced PM Programming. Other courses already available include a two day Intro to OS/2, Overview of Object-Oriented Analysis and Design, C/C++ Programming, DCE Architecture and Internals, UNIX System Optimization and Performance Evaluation, UNIX System calls, Borland's Delphi, and more. Introduction to OS/2 Programming in C Course Overview This five day, instructor-lead course will provide an introduction to programming under IBM's OS/2 operating system, v2.x and higher. The majority of the class will be spent in laboratory exercises, interspersed with lecture and examples. This course provides the beginning concepts which are followed up in the Advanced OS/2 programming course, and the Presentation Manager Programming courses. As a prerequisite, all students must know and have experience with the C programming language. Familiarity with the Workplace Shell, from a user standpoint, is helpful. Students are welcome to bring a copy of their own source code editor. The standard OS/2 'E' and 'EPM' editors will be provided. Target Audience The target audience for this course is Application and System software developers, designers, and maintainers. Course Objectives This course will provide the student with technical and practical experience with:  Multi-threaded programming  The OS/2 Control Program API  File and Disk Handling  Timing Events  Dynamic Memory  Semaphores and Queues  Dynamic Link Libraries For more information, or to schedule a course, contact Gregory Bryant at 800-890-8676, 513-698-5108, or fax 513-698- 4490. Distributed Systems Laboratories is a division of Delta Music Systems, Inc. (OS/2 is a registered trademark of IBM Corporation.) ──────────────────── SpeedPascal/2 version 1.0 SpeedPascal/2 is a Borland Pascal 7.0 compatible compiler and an integrated development environment (IDE) with a host of features designed to ease the task of writing OS/2 Presentation Manager(TM) programs. At last, you can use the productive programmer's language of choice on the world's most productive operating system: NO runtime royalites! NO huge support .DLLs! NO lengthy compile times for small programs! NO sluggish interpreted code! NO limits! The SpeedPascal/2 environment is a suite of tools centered around a Multiple Document Interface editor. From the IDE, you can access the power of the SpeedPascal/2 tools:  A PM-based MDI editor  An integrated debugger  A 32-bit OS/2 compiler  An OS/2 Assembler  A Resource Editor and Compiler  An Inline Assembler  Context sensitive help We're pretty proud of our IDE here at SpeedSoft. If you prefer to use your own editor, that's fine with us. You still have access to the tools you need:  A command line compiler  A command line assembler  A standalone resource editor  A command line resource compiler SpeedPascal comes complete with electronic versions of:  The User's Manual  The Programmer's Manual  The SpeedPascal/2 Language Reference  The OPML Reference Manual SpeedPascal/2 additionally comes with sample code demonstrating the features of the compiler and OS/2. SpeedPascal/2 for OS/2 is just the beginning for SpeedSoft. We're committed to being a cross-platform solution, and to providing you with the best tools around for any platform. We're currently at work with major vendors of Pascal products to see that your favorite libraries get ported to SpeedPascal/2. More info: Download the information brochure at: Compuserve: OS2DF1 forum, REXX/Other, file SPDBRO.TXT WWW: http://xs4all.nl/~atverm/index.html Demo Available A completely functional demo version is available at the same Compuserve forum and through the Web page. The demo can also be downloaded by anonymous ftp at 130.112.6.2, change to the OS2 directoy and transfer (in binary mode) the file speedos2.zip. Limited Time Offer From now until July 31st, 1995, SpeedSoft is offering the SpeedPascal/2 compiler for the price of $129! (Sorry, we can't discount the manual hardcopies.) After this period the price will be $179. SpeedSoft USA 19528 Ventura Blvd. #133 Tarzana, CA 91356 (818) 887-3034 For all Benelux customers (Belgium, Netherlands and Luxembourg): Horacio Software Aardappelmarkt 23 NL-3311 BA Dordrecht Nederland Tel/Fax : +31 ( 78) 147059 Compuserve : 72072,30 For all other European customers: SpeedSoft GbR Nrnberger & Partner c/o Rene Nrnberger Arthur-Strobel-Str. 12 09127 Chemnitz FAX : +49 (371) 2176173 Compuserve: 100614,306 Internet: speedsoft@special.infox.com Support: support@special.infox.com Beta-Tester: speedpascal.beta@special.infox.com ──────────────────── Technical information about DB2 for OS/2 on WWW More than 300 pages of technical information about DB2 for OS/2 (DB2/2) Version 1 available at WWW from http://www.nta.no/brukere/olea/db2book. The material is offered as eight PostScript files, and ranges from beginners tutorial to quite complicated. Areas covered include:  Tables, views, indexes  QM procedures and panels  QM command interface  All aspects of SQL  Programming with REXX, C, C++, and Smalltalk  Logging and recovery  Date and time arithmetic  Intro to set theory and relational algebra  And more... A lot of the material applies to other DB2/CS products (e.g. DB2/6000). A significant subset applies to any SQL implementation (DB2/MVS, Informix, Ingres, Oracle, Rdb, Sybase, etc). ──────────────────── RexxBase Has Been Updated to Version 2.02 June 15, 1995 American Coders, Ltd POB 97462 Raleigh, NC 27624 USA American Coders, LTD release an update to its shareware program RexxBase. RexxBase is a REXX API DLL that allows REXX programs to use dBase III and IV DBF, DBT, NDX and MDX files. This update contains several bug fixes, program enhancements and documentation changes. The newest release can be found in the file RXBAS202.ZIP. The following is a list of the program changes.  Corrected documentation error. RexxBase.SkipDeleted switches use YES or NO.  Corrected DLL to reflect documentation: RexxBase.dBaseFileType should be RexxBase.dBaseFileFormat. Use RexxBase.dBaseFileFormat.  Corrected DLL to reflect documentation: RexxBase.dBaseFileType default value is "dbase3".  Improved GotoRecord function. Specifically TOP parameter and indexes now in synch.  CREATE and PACKING functions failed for dBase IV memo fields.  Corrected documentation error. References to RexxBase.AllowDuplicateIndex corrected to RexxBase.AllowDuplicateIndexes.  Corrected Reindex problem. Not building control blocks correctly. Function required closing DBF before using again.  Patched Reindex function memory leak.  CreateDBFFrom function required closing source DBF before using again.  Restored CloseAllDBF to unregistered version.  Filter routines recalculated dates twice to produce incorrect results(rv).  REXXBASE.EXE concatenated INDEX filenames incorrectly. ──────────────────── OS/2 USERS GROUP MEETINGS [Editor's note - EDM/2 does not normally publish dates of SIG meetings, but these dates were posted well enough in advance and contain topics that deal with application development, so we made an exception by eliminating the non- relevant dates.] New York, NY Jul 17 Presentation Manager and Kernel API overview Aug 21 Intro to REXX + REXX and the Workplace Shell Sep 18 Debugging with IPMD Oct 16 C++ User Interface Class Library All meetings will be held at the offices of the IBM Corporation 590 Madison Avenue in New York City, starting at 6 PM. All are welcome. Call the NYPC Voice BBS at 212-533-NYPC and enter menu code OST for last minute changes. For other information call Bill Zack at 203-255-2979. ──────────────────── Safetynet's StopLight is Now Available Safetynet, Inc. has made available for Internet downloading its StopLight for OS/2 security software. StopLight for OS/2 goes far beyond Workplace Shell protection, and provides:  file and directory-level security (Read, Write, Create, Delete, Execute)  for all OS/2 modes including PM, command prompt (OS/2 and DOS), dual boot, and Win-OS/2  OS/2 2.x and OS/2 Warp compatibility  unlimited users per workstation  administrator and user security levels  audit trail  hard disk lock prevents booting from diskette to access the hard disk  data encryption (USA version only)  keyboard lock  dual boot security for identical security when booting into DOS or OS/2  much more... The full product (without hard disk lock) is available for evaluation from our WWW and FTP sites. If you need more information, please call our tech support department at 1- 800-OS2-SAFE or send e-mail to support@safe.net. http://www.safe.net/safety/ ftp://ftp.safe.net/pub/safetynet/ ──────────────────── EDM/2 Now Distributed to BBS's in Denmark EDM/2 announces the addition of Jesper Nielsen as a new network distributor. Jesper's responsibility is in the distribution of EDM/2 to various bulletin boards in Denmark. Send email to afdata@pop02.ny.us.ibm.net for more information. ──────────────────── Comprehensive OS/2 TCP/IP application homepage The TCP/IP applications list has been converted to HTML and moved. It is available at http://wc62.residence.gatech.edu/sorensen/tcpip.html. This page lists all known shareware/freeware/demoware TCP/IP applications for OS/2 and is catagorized. It is updated based upon when hobbes.nmsu.edu cleans its /incoming directory. Please e-mail any suggestions, additions, changes, etc. to sorensen@wc62.residence.gatech.edu (or use the mailto: tag on the page). The author is also on the IRC channel #OS/2 as No_one. FYI - the text based version is outdated, and the wc51 server should no longer be considered stable. ═══ 5. How Do I Get EDM/2? ═══ How Do I Get EDM/2? EDM/2 can be obtained in any of the following ways: On the Internet  All back issues are available via anonymous FTP from the following sites: - hobbes.nmsu.edu in the /os2/newsltr directory. - ftp.luth.se in the /pub/os2/programming/newsletter directory. - generalhq.pc.cc.cmu.edu in the /pub/newsletters/edm2 directory.  The EDM/2 mailing list. Send an empty message to edm2-info@knex.mind.org to receive a file containing (among other things) instructions for subscribing to EDM/2. This is a UUCP connection, so be patient please.  IBM's external gopher/WWW server in Almaden. The address is index.almaden.ibm.com and it is in the "Non-IBM-Originated" submenu of the "OS/2 Information" menu; the URL is "gopher://index.almaden.ibm.com/1nonibm/os2nonib.70". On Compuserve All back issues are available in the OS/2 Developers Forum 2. IBM Internal  IBM's internal gopher/WWW server in Almaden. The address is n6tfx.almaden.ibm.com and it is in the "Non-IBM-Originated Files" menu; the URL is "gopher://n6tfx.almaden.ibm.com/1!!nonibm/nonibm.70". On BBS's  From BBS's in Denmark, send email to Jesper Nielsen at afdata@pop02.ny.us.ibm.net for a location near you. How do I Get EDM/2? - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6. C++ Compiler Review (Part 1) ═══ ═══ 6.1. Introduction ═══ C++ Compiler Review (Part 1) Written by Gordon Zeglinski Introduction This issue kicks off our look at the various C/C++ compilers for OS/2. We will look at VisualAge C++, Watcom C/C++ 10a, Metaware High C/C++ 3.32 and hopefully Borland C++ 2.0. We start by giving an overview of the development environment and the features of each compiler. After we look at what each compiler has to offer, we will compare the executables generated by each compiler (size and speed) as well as the compile times for each compiler. These tests will consist of compiling various OS/2 utilities (like unzip, zip, POVRay, sysbench, etc.), a C++ test program, a Standardized Template Library (STL) example, and the source code to AdeptXBBS. In this issue, we will look at Watcom C/C++ version 10 and IBM's Visual Age C++ (C-Set++ 3.0). Before we start, I'll state my own compiler background. For the last 2 years, I have been using C-Set++ 2.x to develop OS/2 code. I've become very familiar with this environment, and invariably I will use it as the base to which I formulate qualitative statements. A review of C-Set++ 2.1 appeared in volume 1, issue 5 of EDM/2. All the compilers will be run on a 486 DX2/66 MHz PC clone with 20M of RAM running Warp Fullpack. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.2. Visual Age C++ for OS/2 version 3.0 ═══ Visual Age C++ for OS/2 version 3.0 A full install of Visual Age C++ (VAC++) requires about 180M of disk space. It generates only 32-bit OS/2 programs. This package is the successor to C-Set++ version 2.1. Thus, comparing it to C-Set++ 2.1 is natural. VAC++ has many new features over C-Set++ 2.1. Officially they include:  Performance improvements in the compiler and linker  Direct to SOM (DTS) support in the compiler  New Class Browser  Live Parsing Editor  The Visual Builder - an object oriented graphical program code generator  "Open Class Library" - allows C++ applications to be portable to multiple platforms (Unix, Power PC, Windows 32-bit, etc.) when VisualAge becomes available for these platforms  Database access classes  Multimedia classes  Workframe 3.0 Installation over C-Set++ 2.1 was somewhat of a pain. The old copy of C-Set++ must be completely deleted and all references to it have to be removed from CONFIG.SYS. The install uses the standard CID used by many new IBM products. Other than manually having to clean up the old install, it's a fairly simple installation. The Warp toolkit, workframe, compiler and related tools are all installed at the same time. Once you setup the install, it will successfully complete unattended. Considering it takes about 30 minutes to install off a double speed CD drive, it's great being able to leave the machine alone and go do something useful. One problem with the Warp toolkit is that it messes up the environment variables need by the SOM Developer's Toolkit (if you have this toolkit installed). Some manual tweaking will be necessary to get the SOM toolkit working properly again. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.3. Compiler ═══ Compiler As in C-Set++ 2.1, the compiler needs a fair bit of RAM and is primarily disk bound on this system. The compiler is about 1.3 times slower compiling a large C based project than C-Set++ 2.1. Also the compiler seems to be more RAM hungry as well. However, the compiler is noticeably faster during the linking of C++ code that uses templates. In addition to the DTS support, the C runtime library now has new functions that improve heap maintenance. In addition to multiple heaps, heap checking functions are now supported. POSIX locale's and support functions have been added as well. I've run into several optimizer bugs while testing the compiler. Fortunately, these bugs have been fixed in the compiler CSD that was recently released. This CSD seems to reduce the compiler's memory consumption slightly as well. I still have one outstanding bug report with technical support: calling PrfOpenProfile() causing an access violation exception. Migrating existing applications to VAC++ may require a bit of work. If you are packaging your own copy of the C runtime library, you will need to redo the DEF file that controls which functions you are exporting. As well, the names of several libraries have changed. If you are explicitly linking to any of these libraries, you will have to change their names in your makefiles. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.4. Development Environment ═══ Development Environment The Workframe/2 product has always been a flexible and easily extended programming environment. Version 3.0 is completely different than version 2.1. It combines some some of the concepts of both Workframe 1.0 and 2.1 into a new easy to understand package. However, if you had used version 2.1, you will be scratching your head for a while as you relearn how to do things. (It will be a long while, too.) Something weird is going on with the Workframe, too; everyone I've talked to outside of IBM has significant performance problems. For instance, pop-up menus take seconds to appear, and anything involving filling in the project's filenames takes almost half a minute to finish. During this time, the CPU usage goes way up and the system becomes sluggish. One person inside IBM, who is extremely knowledgeable about the inner's of the Workframe, has reported performance levels that are FAR better than what I've seen. In addition to the performance problem, MAKEMAKE doesn't always generate makefiles that work. I tried to migrate my WF version 2.1 projects to version 3.0, but the process took too long and was awkward. It was faster creating new 3.0 projects from scratch. So, let's start by forgetting all we know about WF 2.1 and throwing away all our previous projects. How hard is it to get a project working? Unlike version 2.1, version 3.0 comes properly configured. To start a new project, you drag out and drop a copy of the project template. Then, you open its settings to configure the working directory, target, and the various build parameters. There are several great new features that should significantly reduce the amount of time it takes to configure a new project or toggle a project from "development mode" to "distribution mode". In version 2.1 one had to have multiple projects one for each mode. This made it extremely awkward to toggle a project's mode. "Build Smarts" in version 3.0 makes this easy. Overall, version 3.0 is more stable and easy to start using than version 2.1. However, the performance problems and the faulty MAKEMAKE result in version 3.0 being less useful than version 2.1. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.5. Debugger ═══ Debugger IPMD version 2.x (the debugger in C-Set++ 2.x) had been the OS/2 debugger to beat. It has many features to aid in debugging PM and multi-threaded programs effectively. Version 3.0 adds to this feature list but removes the "hierarchy browser". (I always wondered why they put a C++ hierarchy browser in the debugger in the first place. It merely duplicates some of the functionality in the browser.) A quick summary of the new features include:  child process debugging - allows processes started by the debugged program to be debugged.  exception filtering - allows the debugger to be configured to ignore certain exceptions  SOM support - allows SOM classes to be monitored in the monitor windows.  Check heap when stopping - the debugger checks the heap when the program stops executing (ie. at a breakpoint or after a step command)  improved call stack now displays more information about the stack usage The heap check function is particularly useful when searching for memory corruption. The strange thing, though, is that there isn't a way to check the heap at any time. The heap can only be automatically checked when the debugged program has been stopped by the debugger. The PM Message Spy and Window Analysis functions are still present and have been redesigned to improve functionality. Overall, the debugger's feature set has been greatly improved. However, it has numerous bugs which make it less useful than the previous version. Following is a brief list of problems I've encountered while moving 3 medium-to-large programs to VAC++:  in asynchronous debug mode, the debugger "looses control" of the PM program. It's as if the debugger can't terminate the debugged program. Trying to "restart" the application results in the application being run until the user terminates the application. Once terminated, the debugger "restarts" the application.  in syncronous mode, trying to step into (pressing "I") a function will result in the debugger hanging.  after an access violation exception occurs in an OS/2 DLL, doing anything other than continuing to execute the program can result in the debugger soft hanging the system.  in syncronous mode, doing a lot of stepping over (pressing "O") can result in hanging the debugger.  occasionally, when hitting a break point in thread 10 or 11 of the program, the debugger crashes with an access violation in one of its DLLs. Note: all of these bugs are reproducible in all 3 of the ported applications while debugging multithreaded C or C++ PM code. Because stepping into or over functions is a fundamental feature of a debugger and the severity of the other bugs I found, I have to say that the debugger is presently useless for PM programs. I don't have any large text mode programs to test the debugger on so it may very well work fine for these types of applications. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.6. Browser ═══ Browser The browser now has support for SOM classes and has a new interface. When running at 1280x1024, it would crash if I tried to open a graphical view. Reducing the resolution to 800x600 (used for the screen shots) allowed the graphical view to work. A nice feature is that the file menu has options to load in the browser information for classes in the Open Class library; this makes the browser a good way to see how third party libraries are designed. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.7. Profiler ═══ Profiler The profiler in version 2.1 was an excellent tool for gathering information on how your program runs. To profile the program it has to be compiled with profiling hooks inserted into the generated code by the compiler. The hooks are used to inform the profiler when a function is called. The result is that a highly accurate map of program execution can be generated, which can then be viewed in a multitude of ways to show time-based or calling-chain information. The major improvements to version 3.0 are support to trace up to 64 threads, trace runtime-loaded DLLs, and the ability to start and stop tracing any time during an applications execution. The only problem with version 3.0 is that it crashes on startup. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.8. Visual Builder ═══ Visual Builder The visual builder is written in Smalltalk and it needs a lot of memory. Most people I talk to say to run the builder well, you need 32M of RAM. I gave it a try on this system the HD was trashing a fair bit and the builder was sluggish, but it looked nice. Its design is very object-oriented and fairly easy to pick up. If it only would use less memory... C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.9. Open Class Library ═══ Open Class Library The Open Class Library is designed to allow one's C++ code to be easily moved from one platform to another. There is a price to pay for portability, though. The library is quite large and needs a fair bit of memory at runtime. For small projects, it may be better to simply write the code to the raw API. The class supports various objects to manipulate the user-interface, collection classes, and I/O streams. The most notable improvements to the user interface support is the inclusion of toolbar support, 2-D graphics, animated buttons and flyover help. Flyover help is textual information that appears by the mouse when the mouse is positioned over a button or toolbar element. Note: a CSD for this component is available. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.10. Documentation ═══ Documentation As usual, the amount of paper documents is kept to a minimum. All documentation is on-line in either INF or BOO format. The INF documents have been refined over C-Set++ 2.1 in that they are now easier to use and search. For the paper junky, Postscript files are included. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.11. Summary ═══ Summary Within days of getting VAC++, a CSD for the compiler and Open Class library were available. The compiler CSD lists the optimization bug I found while compiling unzip as fixed, but it did not fix the problem I have while calling PrfOpenProfile(). As a whole, this package has many excellent features, many of which should make code development much easier and faster. Unfortunately, it presently has too many bugs to act as a replacement for large projects developed under C-Set++ 2.1. Given the C-Set++ team's track record with providing fixes for C-Set++ 2.1, though, I think that once we get a round of CSDs for all the components, this will be the compiler package for OS/2. It's likely that by the time a new project grows to the stage where the bugs I have found are important, there will be CSDs out to fix them. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.12. WATCOM C/C++ 10a ═══ WATCOM C/C++ 10a Watcom C/C++ 10a requires about 180M of disk space for a full installation. This includes support for:  16-bit development for the following platforms: - OS/2 1.x - DOS - Windows 3.x applications  32-bit development for the following platforms: - 32-bit OS/2 2.x - extended DOS - Windows NT - Win32s - Win 3.x using a proprietary windows extender - Novell NLMs - Autocad ADS applications [Editor's note - I believe it also supports QNX development.] The package includes a multi-platform GUI and text mode debugger, a class browser, a profiler, a GUI and text mode editor, a windows resource editor, OS/2 2.1 toolkit, SOM 2.0 developers toolkit, and Microsoft's foundation classes for Windows. (Yes, the Windows development tools are richer than the tools for OS/2 PM development.) There are no message "spy" utilities, window hierarchy utilities, etc. for PM development. IBM's Developers Connection CD's include basic applications that allow the developer to spy on messages, etc. Having PM-related debugging features integrated into the debugger is advantageous when developing PM apps, though. Also, the OS/2 version of the text editor isn't PM based. The biggest letdown with the package is that there are more Windows utilities than OS/2 ones. The main components of the package, however, have native OS/2 support. A complaint that I have is that the installation program needs to be polished. While it is capable of copying the most of files to the HD, it only partially configures the system. It doesn't set the INCLUDE or LIB paths at all, configure the DOS AUTOEXEC.BAT, or setup the Windows environments (when installing Windows cross platform support). Also, when running Warp you have to edit the CONFIG.SYS after installing Watcom but before rebooting. You must move the ...\WATCOM\BINP\DLL;...\WATCOM\SOM\LIB to the end of your LIBPATH otherwise the WPS will hang when you reboot due to a SOM version difference. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.13. Compiler ═══ Compiler The compiler is lean and mean. Compile speed has been greatly improved over that of Watcom C/C++ 9.5. In fact this compiler is now considerably faster than C-Set++ 2.1. As with version 9.5, the compiler is easy on your HD because it is primarily CPU bound. Thus, a faster CPU will have a dramatic effect on compilation time. The part I like the most about the compiler is its cross-platform capabilities. The 32-bit OS/2 compiler can generate code for any 32-bit target platform. Similarly, the 16-bit compiler can generate code for any 16-bit target platform. In fact, I used the 16-bit OS/2 compiler to create a Windows 3.x 16-bit applications. (If you have to write Windows programs you might as well use an OS/2 native development environment!) Another bonus is that the package ships with an assembler. Combining the 16-bit C/C++ compiler with the assembler, you have a great OS/2 device driver development environment. (It beats MSC 6.0 + MASM any day, in my opinion.) C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.14. Development Environment ═══ Development Environment The IDE is somewhat similar to the Borland C++ IDE - it's easy to use and fairly intuitive. Several targets can be grouped into a single project. However, the configuring the IDE is not straightforward. I couldn't find any menus or documentation on how to add support for different tools into the IDE. By default, the IDE expects all C and C++ files to use the suffixes .C and .CPP respectively. This is annoying if you want to distinguish between the "type" of C code. An interesting feature, though, is that you can assign specific compiler switches to individual files. Typically, IDE's only allow you to configure a set of swtiches for a given file type. All .C files must then use the same switches. This is not the case for the Watcom IDE. My biggest complaints about the IDE are that it can crash when you least want it to (usually right after making a lot of changes to the project), it uses EPM as its editor, and it needs a bit more polish. Once it's polished up a bit and configuration is made easier it should be great. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.15. Debugger ═══ Debugger The debugger has both a PM and text mode version. The PM version, though, is essentially the text mode debugger in PM version. It lacks any PM-specific features and is rather plain. A useful feature is that it can execute macros when a breakpoint is encountered. Its biggest feature is that the OS/2 native debugger can be used to "remote" debug DOS and Windows programs. This "remote" debugging can be performed on a networked machine or on the local machine. Unfortunately this feature doesn't work completely under Warp. Also, debugging multi-threaded programs under Warp needs a special command line switch (see http://www.watcom.on.ca for info). These problems are supposed to be fixed in version 10.5, though. The debug information can be stored in SYM files, giving you the advantage of not having to rebuild the executable to make a distributable form of your code. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.16. Browser ═══ Browser The browser lets you view inheritance information and the relationship between functions in a source file. The biggest problem with the browser is that it includes all the structures and classes. This can make it quite hard to find the class you are interested in, if you include or . While you can selectively remove objects from the inheritance view, there is no way to remove or include all objects using criteria such as where they are defined, how they are defined, etc. The result is that one often has to remove all the objects from the inheritance view and then add back those that one is interested in. An editor can be launched from the browser to modify the source code. (Note: the IDE crashed while launching the editor, but I have not been able to reproduce this.) C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.17. Profiler ═══ Profiler The profiler displays the data gathered by the sampler. Unfortunately the sampler didn't work properly under Warp. While trying to sample a multi-threaded PM application, the sampler would report an internal error when the sampled program terminated. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.18. Documentation ═══ Documentation The basic package includes compiler and tools on CD along with a getting started book. The documentation package is extra. It includes the following:  Watcom Graphical Tools User's Guide  Watcom Tools User Guide  Watcom C++ Class Library Reference  Watcom C/C++ User's Guide  Watcom C Library Reference  Watcom Linker User's Guide  Watcom Programmer's Guide  Watcom Debugger User's Guide  Watcom C Language Reference *  The C++ Programming Language, second Edition by Bjarne Stroustrup (see note below). Note: Bjarne Stroustrup's book is not found in the online OS/2 documentation, of course. The book The C++ Programming Language is an excellent reference and is not specific to the Watcom compiler. The rest of the books are rather hard to follow, though. It's sometimes hard to find the information that deals with the particular host and target environment you are using. I suppose this is the price one pays when the package supports so many target environments. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.19. Summary ═══ Summary The Watcom package allows one to make applications 16 and 32-bit applications for OS/2, DOS and Windows (NT, et al.) all from an OS/2-based environment. The compiler generates very tight, fast code and does this relatively fast. The incompatibilities it has with Warp should be taken care of in version 10.5. In particular, this package - when combined with OS/2 - makes an excellent DOS and Windows development environment. Its biggest weakness is the lack of any PM specific functions in its debugger, and its Warp incompatibilities. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 6.20. Wrapping Things Up ═══ Wrapping Things Up In this issue, we looked at Watcom C/C++ 10a and Visual Age C++ version 3.0. Watcom C/C++ 10.0 was written before the release of Warp. While its debugger and profiler have problems running under Warp, a new version of Watcom C/C++ has just been released which I am told is Warp-friendly. Watcom C/C++ makes a good multi-platform compiler although it would be even better if it included some multi-platform GUI objects. Visual Age C++ comes with a class library designed for cross platform usage and a nice visual builder that utilizes this class library. The biggest problem with Visual Age C++ is that currently it is has too many bugs in its tools to be useful for handling large applications where a debugger and profiler may be needed. In the next issue we will look at the Metaware High C/C++ compiler and possibly Borland C/C++ for OS/2. We will also see how well each compiler generates various C and C++ code, and test their C++ features. C++ Compiler Review (Part 1) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7. Gearing Up For Games (Part 2) ═══ ═══ 7.1. Introduction ═══ Gearing Up For Games (Part 2) Written by Michael T. Duffy Introduction Welcome back to Gearing Up For Games. Last time we looked into how to use DIVE to blit images to the screen, and then developed a canvas class to make accessing the DIVE buffer easier. This time we will look at how palettes are handled under OS/2, and we will look at decoding and displaying the common bitmap format of PCX to our canvas object. This article builds on the previous one, so the canvas class and DIVE code from the last article will be used in this (and future) articles. Eventually all this code will go together to create a simple game, and the concepts will allow you to branch out and write more meaty games that run natively under OS/2. So without further ado... Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7.2. Palettes ═══ Palettes Before I start my discussion of palettes, I must call attention to an article written for EDM/2 way back in issue 1-1. Raja Thiagarajan wrote an unofficial guide to the palette manager, and I think it may have even been Raja who introduced me to EDM/2 in the first place (Thanks!). I would suggest that you read his article in addition to this one, because it covers some aspects of the palette manager which I will not touch on. I would also like to thank the many people on the Internet who helped me out when I was first learning about palettes. Palettes under OS/2 drove me crazy for several months before I could finally control them to the point that they did what I wanted them to do. Even now they occasionally surprise me; in preparing for this article I discovered that some of the palette code I had been using for a long time would only display a black window in 256 color mode, whereas it worked fine in the 32k color mode I normally use! Just when I think I completely understand palettes, they surprise me with something like this. With the information in this article, the article in EDM/2 issue 1-1, and the API specifications in the manuals you should have enough information to experiment around with palettes. The approach to palettes that I present here seems to work best for games that use DIVE, though there may be some subtle palette tricks that I am unaware of and thus don't cover. Palettes are tricky to use though, so be certain to approach any experimentation with lots of patience, otherwise you may well be driven insane. One final note before we begin: This article makes the assumption that our application will display a 256 color image in our application's window, and use the DIVE API to do it. DIVE is capable of displaying images with more than 256 colors, but for simplicity's sake this series of articles will not cover those situations. What is a Palette? A palette is an array that tells PM what colors to use when displaying an image. A 256 color bitmapped image has one byte for each pixel. This byte tells what color in which to display that pixel. A palette is used to convert that byte into the color information the display hardware needs to actually put the color on the monitor. Palettes can have up to 256 entries. Each entry describes one color, and the color information is stored in three bytes, one each for the component colors red, blue, and green. As they are stored in bytes, each of these component colors can have a value ranging from 0 to 255. Using varying amounts of these three colors, any other color can be described. A bright red would have Red set to 255 and both Green and Blue set to zero. Black is achieved when all three are set to zero and white results from all three being set to 255. All palette activities for an OS/2 program are handled through the palette manager. The palette manager is part of the OS/2 API, and is accessed through the API function calls. A palette manager is needed in a GUI environment because multiple programs may be running at the same time and each program may require different color needs. The palette manager tries it's best to meet these needs. However, the palette manager is restricted by hardware limitations. Many common color modes of video cards have a maximum of being able to display 256 colors at the same time. Although each of these colors may be selected from among 262,144 colors (256k colors), on the graphics card there is a physical maximum of 256 color registers to hold the colors. This has led to the development of two different kinds of palettes: physical and logical. The physical palette is the palette set in these hardware registers. Under OS/2, you can not set the physical palette directly. The palette manager will always determine what colors go in the physical palette, and in what indices these colors are placed. You can read the physical palette with GpiQueryRealColors(), but you cannot set it. [Note: There is one exception to the above. In dive.h a comment explains a way to set the physical palette with undocumented calls. I would urge everyone to avoid this method though, since it is unknown what that code would do in a non-paletted video environment (such as the 32 and 64 bit modes), and this method may not be supported on different hardware (such as the PowerPC, or future Intel based hardware). I have not experimented with the undocumented code in dive.h because of lack of information on the undocumented calls, and because I have been able to achieve the results I need through the palette manager and without messing up the integrity of the desktop.] Although programs cannot access the physical palettes, they can have their own logical palettes. A logical palette is set up in the same format as a physical palette. The logical palette is sent to the palette manager, and whenever your window has focus, the palette manager will change the physical palette to match the logical palette as much as possible. For the colors that do not match the physical palette exactly, the palette manager will decide the closest match, and use that instead. When you request that color 4 be drawn to the screen, the palette manager will look at color 4 in the logical palette, decide what color it most closely matches in the physical palette, and send the new color out to the display hardware. By approaching palettes this way, several applications can share the scarce palette entries at the same time. The palette manager will always try and fulfill the needs of the palette in the active window. Applications whose windows are not active will still have their colors remapped to the closest palette entry, but the palette manager will not use the inactive palettes to determine the contents of the physical palette. Note that remapping is done when an image is drawn to the screen, such as with DiveBlitImage(). If the display is in a 256 color mode, the physical palette changes, and a window is not redrawn, then its colors will be wrong. But what about graphics modes with more than 256 colors? Many users of OS/2 have their displays set to modes that offer 32k, 64k, or even 16.4 million colors. In these cases, there are no color registers to fill. If two applications request palettes of 256 different colors each, both requests can be met without any problem. In these cases there is no physical palette to set, but a logical palette is still required. The system must know what color each of the 256 palette entries in our source bitmap is to be displayed as when it converts each pixel to a 15, 16, or 24 bit color value. How to Use Palettes Under OS/2 Our goal is to use palettes in conjunction with the DIVE API in order to display images in a window. Although DIVE has the calls DiveSetSourcePalette() and DiveSetDestinationPalette(), neither of these calls affects the physical palette in any way. These calls are used to tell DIVE what logical palette it is mapping from and what physical palette it is mapping to. To change the physical palette in 256 color video modes, you must request that the palette manager remap the physical palette as much as possible to a specified logical palette. The steps for the creation and activation of a palette are as follows: 1. Query the device context of your client window with WinOpenWindowDC(). 2. Create a presentation space from the device context with GpiCreatePS(). You cannot use a cached-micro PS (from WinGetPS()). We will use a micro PS. 3. Create an array that describes your palette. (The format for this array is described below.) Use the PC_RESERVED flag to obtain as many of your colors as possible. 4. Create an OS/2 palette from this array and obtain a handle to the new palette with GpiCreatePalette(). 5. Select the palette into the presentation space with GpiSelectPalette(). 6. Make the palette active ("realize it") with WinRealizePalette(). 7. Set the palette DIVE will use for your source bitmap by using your original array with DiveSetSourcePalette(). 8. Tell DIVE what the physical palette is by either using GpiQueryRealColors() and passing the result to DiveSetDestinationPalette(), or call DiveSetDestinationPalette(hDive,0,256,0). 9. Blit your image. During normal processing you have a few tasks to perform to maintain the palette. 10. On WM_REALIZEPALETTE messages, inform DIVE that the physical palette may have changed. Do this with either method described in step 8, though simply calling DiveSetDestinationPalette(hDive,0,256,0) is the easiest. 11. On a WM_ACTIVATE message, if your application is losing focus, free up your palette so that other applications may have as many of their colors mapped into the physical palette as possible. Do this by selecting a null palette into the presentation space with GpiSelectPalette(), and activate this null palette with WinRealizePalette(). 12. On a WM_ACTIVATE message, if your application is gaining focus select and activate your desired palette again so that the physical palette will be remapped more closely to your logical one. Use GpiSelectPalette() and WinRealizePalette() to achieve this. When you are done using the palette and your game is ready to end. 13. De-select your palette from the presentation space by selecting a null palette with GpiSelectPalette(). 14. Destroy the palette with GpiDeletePalette(). 15. Destroy the presentation space with GpiDestroyPS(). Notes Certain things must be considered in each of the above steps. The important ones are detailed below, and the general implementation of the steps can be viewed in the included sample files game2.cpp and os2pal.cpp. Step 1: WinOpenWindowDC() will only work once per window. Multiple calls will fail, so be certain to save the returned device context handle. Step 2: again, make certain that you do not use a cached micro PS. For our purposes, we will call GpiCreatePS() with the flags PU_PELS, GPIF_DEFAULT, GPIT_MICRO, and GPIA_ASSOC. Also, be certain to save the handle to this presentation space and use it throughout your program. This includes using it with the DIVE code that calls GpiCreateRegion(), GpiQueryRegionRects(), and GpiDestroyRegion(). If you create and use separate PSs for palette and DIVE tasks, then in 256 color video modes your blitted images will appear black, even though they will blit correctly in higher color resolution modes. Step 3: the array that describes your palette is an array of unsigned longs. Three of the bytes in the ULONG describe the red, blue, and green components of the color, and the fourth byte is a flag byte. The flag byte tells the palette manager a bit more information about that specific color. For our purposes, we will set all of the flags to the same value. This value is PC_RESERVED, and it signals the palette manager that the color in question may change frequently due to palette animation. Therefore the palette manger will not use these colors for other windows, and you have more control over what colors are put in the physical palette while your window has focus. The formula for placing the four component bytes into a ULONG is: ulArrayEntry = ((ULONG)byFlag << 24) + ((ULONG)byRed << 16) + ((ULONG)byGreen << 8) + byBlue; [Editor's note - you can also use the RGB2 structure for this purpose.] Step 4: with GpiCreatePalette(), for flOptions we use LCOL_PURECOLOR to prevent dithering, but we do not use LCOL_OVERRIDE_DEFAULT_COLORS in order to keep from messing up the desktop colors. For ulFormat we use LCOLF_CONSECRGB as it is currently the only supported format. We pass this function an array of the format described in step 3 above. Step 5: presentation spaces are used to hold display information for a window. If we were to use Gpi drawing routines, the PS would hold the current drawing colors, line characteristics, marker styles, character attributes, and so on. The PS also holds palette information for our window. To tell the PS which palette to use, we call GpiSelectPalette(). Step 6: even though we selected a palette to use for our PS in step 5, this palette is not yet made active. In order to tell the system to use our palette whenever our window has focus, we must call WinRealizePalette(). Step 7: we must tell DIVE which color each byte in our DIVE buffer stands for. This is done with DiveSetSourcePalette(). If DiveSetSourcePalette() is not called, then the default palette is used. If your DIVE buffer does not use the default palette, your results will be unpredictable. Step 8: we must also tell DIVE about the colors of the display hardware. This can be done by retrieving the physical palette with GpiQueryRealColors() and passing it to DiveSetDestinationPalette(). A shortcut to this is to call DiveSetDestinationPalette() with a null pointer to the palette array. This will instruct the routine to call GpiQueryRealColors() on its own. The notes for this step also apply to step 10 and the WM_REALIZEPALETTE message. Steps 11 and 12: When our game loses focus, one of the things we want to do is give the system back as many colors as possible so that other applications may use them. Since we use the PC_RESERVED flag with all colors of our game's palettes, our palette holds a lot of weight with the palette manager. By deactivating our palette when we lose focus, the window receiving focus will have its colors more readily remapped into the physical palette. By specifically reactivating our palette when we regain focus, our colors are set in the physical palette without delay. We need to redraw our window when we regain focus in case we received focus when another overlapping window closed. Such situations lead to a corrupted display if we don't redraw our window. In the future when we have a blit thread continually drawing our image to the window, the explicit redraw will not be needed. Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7.3. The OS2Palette object ═══ The OS2Palette object Many of the above steps can be combined because they always occur after one another, such as GpiSelectPalette() and WinRealizePalette(). Also, we may have to work with palettes other than OS/2 palettes. An example of this are the palettes stored in compressed bitmap files. To simplify palette operations we will use a palette object that hides from us repetitive tasks. For learning about the specific implementation of any of the tasks provided by the palette object, I encourage you to look at the source code for the object in os2pal.cpp. The following functions are those provided by the OS2Palette object. They do not cover every single aspect of palettes, but they provide an illustration of the major points. Also, they provide all the functionality we need for our simple game. Default16(): sets the palette to the first 16 colors of a standard VGA palette. This can be done for initialization and testing purposes. This function illustrates how to organize and set a default palette. Convert(): this routine converts the internal palette from one format to another. The two formats implemented in this code are for plain 8-bit RGB palettes and OS/2 palettes. The former are what you might find in a compressed bitmap (we will use them in the PCX codec), and the latter are what you will pass to OS/2 and DIVE. InitSystemPalette(): this will convert the internal palette to the OS/2 format, create an OS/2 palette, select it into the supplied presentation space, and realize it. UninitSystemPalette(): this will de-select the internal palette from the presentation space, realize a null palette, and destroy the palette. This routine is called on cleanup. SuspendSystemPalette(): this will de-select the palette from the presentation space and realize a null palette, but it will not destroy the palette. This is meant to be used on a WM_ACTIVATE message. Call RestoreSystemPalette() to reactivate the palette. RestoreSystemPalette(): restores the palette suspended by SuspendSystemPalette(). SetAsDiveSource() and SetAsDiveDestination(): these routines are used to tell DIVE about the palette. You will usually have at least two palette objects, one for the DIVE bitmap and one for the screen. If you choose to always set the DIVE destination palette with DiveSetDestinationPalette(hDive,0,256,0), then a palette for the screen is not needed. LoadActualPalette(): this sets the palette to the physical palette. SetValuesFromRaw8(): this will set a range of values from an array of 8-bit RGB values. PaletteFromBlock(), RequestSaveBlock(), and ReleaseSaveBlock(): these three functions package or unpackage a palette into a single block of data. These routines will be used in the future when we look at storing data in a single library file. Currently they may be used to load and save a palette to disk. SetFlag() and QueryFlag(): these inline functions are for setting and getting the value that is copied into the flag position of the colors in an OS/2 format palette. QueryPaletteArray(): this returns a pointer to the internal array that contains the palette. The format of the array depends on the format set with Convert(). One use of this routine would be to supply palette information to a bitmap encoding routine. First, Convert() would be used to set an 8-bit palette. Then this routine would be used to get a pointer to the palette information to send to the encoding routine. QueryLastErrorCode(): Returns the code of the last error that occurred. This error code is specified in errcodes.hpp and the associated message is stored in game2.msg. This error code can be used with PostError(), a routine found in errdisp.cpp. Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7.4. Palettes for Multiple Child Windows ═══ Palettes for Multiple Child Windows Sometimes I am very glad that I don't keep a rocket launcher on hand near my computer. Recently I'm afraid that I would have had to blow my computer into tiny, unrecognizable pieces considering how much grief it caused me. After months of being bothered by the same, nagging palette problem, I finally discovered a solution. The problem arises when you have two child windows, both of which are using DIVE to blit an image to its client area. I ran across this situation while I was writing editors and other utilities for my own game, though such a situation could very well arise in a game with multiple windows. In order to get the same palette to display correctly in both windows, implement a palette as above in the "How To Use Palettes in OS/2" section. The various steps will be split amongst the parent and children windows as follows: 1. The HDC and the HPS for the palette should be derived from the parent window. The palette should be created, selected, realized, and eventually destroyed from the parent window. The parent window will handle suspending and restoring the system palette on WM_ACTIVATE calls. 2. An HDC and HPS should also be created for each child window, and these handles will be used to setup the DIVE blitter among other things. No palettes will be selected into the child windows' HPS's, however. 3. Each child window should have its own instance of DIVE. 4. Instead of setting the DIVE source and destination palettes in the parent window, send messages to the child windows and have them perform these tasks. When you send the message, pass a pointer to the palette array as one of your parameters. Make sure this array is in the OS/2 palette format. 5. Do not use the shortcut of DiveSetDestinationPalette(hDive,0,256,0). You will have to use GpiQueryRealColors(), and then pass a pointer to the returned palette array to each of the child windows so that they can call DiveSetDestinationPalette() with this info. When you need to update the destination palette, only call GpiQueryRealColors() once and pass the same array to both child windows. Every time the parent window receives a WM_REALIZEPALETTE message, it must query the real palette and pass the array to each of the child windows. The child windows should not process the WM_REALIZEPALETTE message. Performing the above steps should allow the same palette to be used for two separate child windows. Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7.5. Graphics Formats ═══ Graphics Formats When graphics are stored to disk, they are usually saved in a special format rather than just dumping memory to a disk file. These formats contain additional information about the graphic such as its size, number of colors, palettes, and other types of info. The graphic data is also usually compressed in order to save disk space. Compression/decompression algorithms are usually called codec's, and each graphic format usually has it's own codec which is different from the others. Codecs differ in their compression ratio, speed, and quality. Certain codecs throw out some of the original information during compression, and then try and guess what is missing when they decompress the image. These are called lossy codecs because they lose some of the data. JPEG is an example of a format with a lossy codec. Lossless codecs generate the same image upon decompression as was given to them for compression. Popular formats with lossless codecs include GIF, PCX, TIF, and PNG. Our code will deal with PCX files. PCX files have the advantage of being supported natively by many paint programs both shareware and commercial. This format was originally developed by ZSoft for their paint program PC Paintbrush, but it was adopted by many other programs as well due to its ease of use. PCX files support monochrome, 16-color, and 256 color images though we will only concern ourselves with the monochrome and 256 color capabilities. PCX files are also good because their compression scheme is very simple to understand. It is not a compression scheme that works well for all graphics though, and many scanned images would wind up larger than where they started if the PCX scheme were used on them. Nevertheless, it will suit our needs nicely. As far as implementation in our sample code is concerned, the PCX encoding/decoding object is derived from a parent class called BitmapPainter. This allows us to treat different graphic formats in the same way. The same calls are used to setup, compress, and decompress graphics no matter what format in which they are stored. Although we will only implement code to deal with PCX files, you could write code to support other formats without having to change much code in programs that already use BitmapPainter-derived objects. Also, our PCX display code will use the Canvas class to hold the decompressed image. The Canvas class was covered in the first article of this series. Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7.6. On to PCX! ═══ On to PCX! PCX files have three parts: the header, the image data, and the palette. Monochrome images only have a header and the image data, since all pixels are either black or white. The Header The header of the PCX looks like this: typedef struct { BYTE byManufacturer; // always 0x0a BYTE byVersion; // version number BYTE byEncoding; // always 1 BYTE byBitsPerPixel; // color bit depth USHORT usXmin; // coordinate of left side of image USHORT usYmin; // coordinate of top of image USHORT usXmax; // coordinate of right side of image USHORT usYmax; // coordinate of bottom of image USHORT usHres; // horizontal resolution of creation device USHORT usVres; // vertical resolution of creation device BYTE abyPalette [48]; // color palette for 16 color images BYTE byReserved; BYTE byColorPlanes; // number of color planes in the image USHORT usBytesPerLine; // line buffer size USHORT usPaletteType; // gray or color palette BYTE abyFiller [58]; } PCXHEADER; The header is exactly 128 bytes long, and is always located at the very beginning of a PCX file. A more detailed meaning of each field is as follows: Manufacturer: always 0x0a. Checking this byte is the only way to really tell whether or not a file is a PCX file. Version: this variable is meant to tell what version of PC Paintbrush created the file. A zero indicated PC Paintbrush version 2.5. A two or three signifies version 2.8. A two means that the abyPalette field contains palette information whereas a three means that the image is either monochrome or meant to be displayed with the default palette of the device. A five indicates that the PCX came from version 3.0 or better, and this is the only version that can support 256 color images. Encoding: this is always 1, though the field was put here in case ZSoft wanted to add a different kind of codec to the PCX specification. A 1 signifies that the image uses a Run- Length Encoding (RLE) codec. BitsPerPixel: this tells how many bits are used to make a single pixel in a single color plane. Monochrome images only have black and white pixels, so they have 1 bit per pixel. 256 color images have 8 bits per pixel since they need entries from 0 to 255. 16 color images need 4 bits to generate a number between 0 and 15. However, 16 color images are stored in four color planes, so they only have one bit per pixel in each color plane. Monochrome and 256 color images only have a single color plane, so their bits per pixel value is a count of their total bits. Color planes are explained below. Xmin, Ymin, Xmax, and Ymax: these values give the boundaries of the image. Usually Xmin and Ymin will be equal to zero. These coordinates represent the origin, and the origin is located in the top left hand corner of the image. Xmax and Ymax give the right and bottom edges respectively. These coordinates are inclusive. This means that if Xmin is 0 and Xmax is 639, a row of this image runs from pixel 0 to pixel 639 inclusive. It has a width of 640 pixels. Therefore when computing the width or the height from these values, be certain to add 1 to get the correct answer: (i.e. usWidth = usXmax - usXmin + 1). Hres and Vres: these values give the dimensions of the hardware that generated the image, and are basically useless values. Palette: this array of bytes contains a palette for 16 color images. PCX palette have a red, blue, and green byte for each color, so 3 color bytes times 16 colors leads to the 48 byte length of this array. The PCX format was developed before 256 color images were available on the PC. As a result, the designers of the PCX format did not leave room for a 256 color palette. To solve this problem, the palette for 256 color images was tacked onto the end of the PCX file, and this array was not used. Since we will not be using 16 color images, we will ignore this field. ColorPlanes: this tells how many color planes are used for the image. Color planes are used because of the way hardware is setup in EGA and VGA cards for the 4 and 16 color modes. A pixel in these modes has one bit in each bit plane instead of having its bits side by side as in 256 color mode. The bits in each of the bit planes are combined to get the color index of a single pixel. If you haven't encountered bit planes before and don't understand how they are set up, I would suggest looking in a book on EGA/VGA cards. They take a little bit of time and space to explain clearly, and since we are not dealing with 16 color formats I will not cover them here. BytesPerLine: this field tells how many bytes are in a single decompressed row. This is useful in monochrome and 16 color images, but the bytes per line in a 256 color image is the same as its width. PaletteType: this simply tells whether or not an image is meant to be displayed in color or gray scale. A value of 1 indicates gray scale and a value of 2 indicates color. The Codec The algorithm for decoding a PCX image is simple. You take the current byte of the image data and look at it's top two bits. If they are both clear, then you write that byte out to your canvas. If they are both set, you clear them and that byte becomes your duplication count, or run length. Take the next byte and copy it to the canvas "run length" number of times. Repeat the process until you have enough bytes to fill a row. Move to the next row down and repeat the process for that row. Do this until there are no more rows left in the image. The only catch to the above algorithm is when you run across a single pixel that has information in one or both of the top two bits. Bytes for these types of pixels are handled as having a run length of 1, since the entire 8 bits of the byte can be stored in the byte following the run length byte. Encoding a PCX is just the reverse of this process. Start at the first pixel in the top row of your image. Count how many times this pixel color appears in a run. Do not let this run length go over 63, because you can only store the run length in 6 bits (the top two will be set). If the run of bytes is greater than one, set the top two bits of the run length and send it to your encode buffer. Next send the byte to duplicate. If there is only one byte, check and see if any of the top two bits are set. If one or both are set, output a run length of one, and then output the byte. Otherwise, simply output the byte. Repeat until you are done with a row, and then move to the next row down. Continue in this way until all rows are done. This same codec is used in both monochrome and 256 color images, though the pixels are stored slightly differently. For monochrome images, each byte contains 8 pixels, since only one bit is needed for each pixel. If the bit is set, it denotes a white pixel. If it is clear, it denotes a black pixel. The most significant bit is the left most pixel in that byte, and the least significant pixel is to the right. Bits can be tested in the byte with a bitwise "and" operation. 256 color images have their bits stored right next to each other. Since it takes 8 bits per pixel, the entire byte is used for the color index. You can decode the row directly onto the surface of a Canvas. The Palette In monochrome images, there is no palette because the pixels can either be white or black. In 256 color images, the palette is attached to the end of the PCX file. The palette is stored as one byte each for the red, green, and blue components of each color, in that order (RGB). There are 256 colors possible, so the palette is 3 color bytes * 256 indexes = 768 bytes long. To make sure that you aren't reading image data instead of palette data, PCX files have the byte 0x0c right before the palette information starts. To read a PCX palette you find the end of the file and move back 769 bytes. Check that byte to see if it equals 0x0c. If it does, read the next 768 bytes as your palette. Byte Order One last note about palettes: To use the palette attached to the PCX file, you will need to convert it to an OS/2 palette. An OS/2 palette has the elements Flag-Red-Green-Blue stored in a ULONG. However, Intel based PCs store USHORTs and ULONGs in a byte reversed order, and if you access the OS/2 palette as an array of bytes instead of an array of ULONGs, then you must take this into consideration. The byte reversal scheme works like this: Suppose you have a variable at memory location 0x1000. A byte would be stored at location 0x1000, a USHORT would be in locations 0x1000 and 0x1001, and a ULONG would be stored in locations 0x1000 through 0x1003. Placing a byte is no problem; just put it in location 0x1000. A USHORT is made up of two bytes. One byte handles the lowest 8 bits, and the other byte handles the highest 8-bits. When an Intel based PC stores the USHORT in memory, it will place the lowest 8 bits in the first byte (0x1000), and the highest 8 bytes in the next byte (0x1001). A number under 256, say the value 42, would be stored in the lowest 8 bits of the USHORT. A pointer to our USHORT would have the value of 0x1000. When the USHORT is accessed with the pointer, then the bytes will be placed in their correct order so that the value of 42 is retrieved. However, what if we typecast this pointer and use it as a pointer to type BYTE. A BYTE can hold the value of 42, but it is only looking for a single byte in memory (8-bits). Since the bytes are reversed and the lowest byte is at location 0x1000, the byte pointer can read the value fine. If the bytes were not reversed, then the value at 0x1000 would hold a zero and the byte pointer would receive the wrong value. This is the rationale behind the reversed byte order scheme. For this same reason, the bytes that make up a ULONG are reversed twice. You can look at ULONGs like two USHORTs, but the USHORTs are reversed so that the lowest 8 bits of the ULONG are placed at the first memory location (0x1000). What all this means for palettes is this: You access the bytes in a PCX one after another in the order of Red, Green, Blue. byRed = *(pbyColor + 0); byGreen = *(pbyColor + 1); byBlue = *(pbyColor + 2); If accessed with a byte pointer, the bytes in an OS/2 palette are accessed in the order Blue, Green, Red, Flag. byBlue = *(pbyColor + 0); byGreen = *(pbyColor + 1); byRed = *(pbyColor + 2); byFlag = *(pbyColor + 3); For implementation of a PCX decoder and encoder, check out the sample source code that comes with this article. Handling the header, codec, and palette can all be seen in pcx.cpp. The handling of the byte reversal scheme can be seen in the Convert() routine of os2pal.cpp. Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7.7. The PcxPainter object ═══ The PcxPainter object The PcxPainter object is derived from the BitmapPainter object. All of the member functions called by a program are virtual functions in the BitmapPainter object, so you could just as easily replace the PcxPainter object with a GifPainter or TifPainter, and not have to change much of anything else in your program. The functions of the BitmapPainter object are: AssociateCanvas(): this routine tells the BitmapPainter object what canvas is to be used for the uncompressed graphic. If the canvas is not large enough for the graphic, the information outside of the canvas will be discarded. DissociateCanvas(): this routine clears the internal pointers to the canvas. AssociateBuffer(): this routine is used to tell the object where the encoded graphic is in memory. Such a buffer might be created by finding the size of a PCX file, allocating that much memory, and then reading the entire PCX file into that buffer. In the PcxPainter object, the AssociateBuffer() routine also checks the buffer to see if it contains valid PCX information before it accepts the buffer. If the buffer does not contain valid information or is of an unsupported PCX type (16 color), then the routine returns with an error. DissociateBuffer(): this clears the internal pointers to the buffer. PaintCanvas(): this instructs the object to take the compressed graphic specified in AssociateBuffer(), decompress it, and display it to the canvas specified with AssociateCanvas(). This routine also determines the palette of the graphic, and stores it in an internal buffer. EncodeCanvas(): For PcxPainter, EncodeCanvas() will take the entire canvas specified with AssociateCanvas(), encodes it in PCX format, and stores it in an internal buffer. It allocates and uses an internal buffer because the size of the compressed graphic is not known when the routine is called. EncodeCanvas() will return a pointer to the internal buffer, as well as the size of this buffer. FreeEncodeBuffer(): This causes the internal buffer that EncodeCanvas() allocated to be deallocated. QueryBitmapStats(): This routine looks at the information in the buffer specified by AssociateBuffer(), and fills in the provided structure with the bitmap width, height, and color depth. MonoSetWhite() and MonoSetBlack(): These routines set the color index to be used for the black or the white pixels when a monochrome bitmap is decoded. Since the target canvas is a 256 color canvas, values from 0 to 255 may be specified. QueryPaletteBuffer(): This routine returns a pointer to the internal buffer with the palette info. The palette data is stored with three bytes per palette entry, and the bytes specify the red, green, and blue components of the color respectively. QueryLastErrorCode(): This returns a code for the last error encountered, and its value can be used to look up the error code from the resource file's string table. The codes are specified in errcodes.hpp and the error strings are specified in game2.msg. Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7.8. Conclusion and News of the Future ═══ Conclusion and News of the Future Well, that's about it for my second article in Gearing Up For Games. As always, comments, flames, and questions are welcomed. I can be reached at the email address listed in the "Contributors" section. Next time I will probably look into threads, semaphores, timers and other multitasking concerns. If I have time, I may start on how to draw sprites as well. In the future, I will be looking into the fullscreen support aspects of DIVE. I recently added fullscreen support to a project I'm working on, and it was a fairly simple process. It took about 15 minutes to write all the code needed to implement fullscreen support, an hour to get the darn thing to compile (problems with parameter passing since header files for fullscreen support don't exist yet), and another hour working out undocumented pitfalls of fullscreen modes. I'll be certain to give a full report in a couple of months as availability of the Games SDK draws closer. It looks like the Games SDK will be released through the DevCon CD subscription program. For those of you unfamiliar with DevCon, it is subscription that comes four times a year, and each "issue" contains several CDs with information, toolkits, and sample code on them, as well as a paper newsletter with articles on various aspects of developing for OS/2. I included ordering information in my last article, so I won't repeat it here. I don't know if the Games SDK will be released through other means other than DevCon. I think that IBM should release it freely, but that is just my (and a lot of other people's) opinion. Also, I have recently learned that IBM has released joystick drivers for OS/2. The drivers can be obtained by anonymous ftp to ftp01.ny.us.ibm.net in the /pub/games directory. The filename is JOYSTICK.ZIP. I haven't found any programming information for these drivers yet, but I'll be sure to fill everyone in on what I can find as soon as I find it! Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 7.9. Whoops! ═══ Whoops! Well, looks like I made a few mistakes in the code for my last article. Time to come clean on them: 1. In game1.rc, the ICON statement should read: ICON ID_APPMAIN game1.ico 2. The file game1.msg was not needed. GAME2 uses .MSG files however. 3. The Canvas::QueryWidth() and Canvas::QueryHeight() methods should return USHORTs instead of SHORTs. 4. In the article itself, the colors were wrong for the picture of the sample code's output. This happened because I did not convert the palette of the bitmap to the system palette before I submitted it. Compiling the Sample Code With the Watcom Compiler I did not give instructions on how to get the sample code to compile with the Watcom compiler (which I actually use by the way.) To use the DevCon toolkit with Watcom, the following things must be done: In Your C/CPP Compilations First, for all of your C and CPP files that use the DevCon Warp Toolkit, add: #define __IBMC__ before you include any of the DevCon header files. Note that there are two underscores both before and after the "IBMC", not just one. Also make sure that the compiler is looking in the \H directory of your DevCon toolkit for header files, not in the WATCOM\H\OS2 directory. In The File mmioos2.h Find the section titled : "Country codes (CC), languages (LC), and dialects (DC)." You will notice that some of the defines in this section begin with a zero. This causes the Watcom compiler to read these numerals as octals (as any good ANSI compiler would ). Remove these zeroes. Therefore the definition: #define MMIO_CC_USA 001 would become #define MMIO_CC_USA 1 This needs to be done from MMIO_CC_NONE (value of 0) to MMIO_CC_TURKEY (value of 90). In Your Linker setup Don't forget to include the library mmpm2.lib when you link. This can either be done in the IDE in the menu "Options \ OS/2 Linking Switches \2.Import, Export and Library Switches" in the "Libraries(,):[libr]" window, or in the command line of your linker with the statement "libr mmpm2.lib" (without the double quotes, of course). Also, be certain to have a large enough stack for your program. The default stack for the Watcom IDE is about 8K I think. The MMPM/2 documentation suggests at least a 16Kb stack, but I compile with a 32K stack. Gearing Up For Games (Part 2) - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8. Programming the WPS Using the UICL ═══ ═══ 8.1. Introduction ═══ Programming the WPS Using the UICL Written by Juergen Riedl Introduction About one year ago I posted a question onto the net asking whether or not it is possible to develop WPS classes with IBM's User Interface Class Libraries (UICL), but even IBM itself couldn't give me an answer, so I forgot about it. In April this year, however, I was offered by the OS/2 User Group of the Technical University of Munich (Bavaria / Germany) to give a review of the book about the UICL entitled OS/2 C++ Class Library, Power GUI Programming with C Set++ by Leong, Law, Love, Tsuji and Olson. I remembered my wish a year ago, started my first simple steps to program the WPS using the UICL, stumbled into some mysterious probems. But with some luck I got it to work ("1% inspiration but 99% transpiration", Thomas Edison). This submission is the attempt to share my experiences with you, in the hope to enable you to easily write applications which are completely integrated into the WPS using the UICL. If this is to your taste then follow me on the journey to see that it is not too difficult. Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.2. The Paradigms ═══ The Paradigms Frequently, IBM has created sophisticated APIs but never invested sufficient effort to publish the required documents to enable programmers to use them. This situation has slightly changed for the WPS [1, 2] and has been considerably improved for the UICL [3]. The WPS represents the paradigm which enables you to use your computer in an document-centered fashion. But due to the lack of good guidance, developing WPS classes is a rather difficult task. The PM API is abstracted from you; you don't see any function responsible for message processing; you don't even see an easy way to extent their functionality. In my opinion this is the reason why 99% of all OS/2 applications follow the old PM style paradigm. Any WPS programmer will be seen as a guru, which in fact they are. I made my first steps of WPS programming guided by the book Client/Server Programming with OS/2 2.1 by Orfaly and Harkey. But I didn't like WPS programming very much - even small bugs in my classes required rebooting the machine. So it was burdensome. This changed dramatically with the advent of OS/2 3.0 Warp which designed the WPS as a DSOM server. Instead of rebooting the machine as before, the WPS was usually the one restarted. Along the way, I learned to like the UICL, which presented the obvious question: why should I program in the old PM style after having tasted the sweet flavour of the UICL? At a first glance, the UICL appears to be a wrapper of PM. If you take the time, though, you will realize that it is more: it provides the means of creating sophisticated classes with few lines of code instead of hundreds as the pure PM programmer is forced to do. Even though the UICL is that powerful it seems to me to contradict the aim of the WPS, which is to support the document-centered paradigm. All of the "programs" should be seamlessly integrated into the shell, like extensions; no one should start any application first and generate the document afterwards as in the past. But with the UICL the obvious way is to generate an EXE which will be attached to the usual program notebook of the WPS. Wouldn't it be nice to be able to open a customized notebook of this application as any other WPS object? Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.3. Mastering the First Step ═══ Mastering the First Step You have read the file README.1ST and set up your environment accordingly? All right, lets start. What do we want to do? We want to develop a WPS application comprising a notebook and a frame window. Will this application be a "killer application?" No, it is only a sample to show how to implement the WPS and UICL combination. The development of the "killer application" is up to you. But it will become far easier in my opinion. We will begin by subclassing WPAbstract. WPAbstract provides two notebook pages, the Window page and the General page. For the first step, we will extend this class to open an ordinary "Hello World" frame window by double clicking on its icon. After that we will examine how to add customized notebook pages to the already defined ones. And, of course, all of this will be done using the UICL! Since we are dealing with the WPS we must use the SOM IDL. (The new VisualAge++ compiler may change this with its Direct To SOM capability) But it is not difficult to follow the code; examine the file OS2UG.IDL (for information on SOM and WPS please read EDM/2 issues 2-8 and 2-9): interface XWPS : WPAbstract { #ifdef __SOMIDL__ implementation { //# Class Modifiers majorversion = 1; minorversion = 2; dllname = "os2ug.dll"; metaclass = M_XWPS; callstyle = oidl; passthru C_xih_before = "#include "; //# Method Modifiers wpModifyPopupMenu : override; wpMenuItemSelected: override; wpOpen : override; wpAddSettingsPages: override; }; #endif // __SOMIDL__ }; As you might assume looking at the class declaration interface, XWPS : WPAbstract sounds like class XWPS : public WPAbstract in C++. Outside of the implementation block, neither additional instance variables nor functions are declared. For that reason, no release order section is required. To be compliant with the WPS call style, set callstyle = oidl. If you will write CORBA-compliant DSOM classes, you must set callstyle = corba. But this doesn't apply to WPS GUI classes yet. During this article, we will override some WPS instance methods, which begin with the letters wp. For the first step, we will focus on three methods; the method wpAddSettingsPages() is the subject of the second step. Each of them will be discussed separately. Now take a look onto the metaclass declaration interface M_XWPS: interface M_XWPS // same as interface M_XWPS: M_WPAbstract { #ifdef __SOMIDL__ implementation { //# Class Modifiers majorversion = 1; minorversion = 2; callstyle = oidl; //# Method Modifiers wpclsQueryStyle: override; wpclsQueryTitle: override; wpclsQueryDefaultView: override; }; #endif // __SOMIDL__ }; Here you see that SOM is slightly different from C++. It provides different types of classes and subsequently different types of methods: class methods and instance methods. Class methods are tied to metaclasses. Metaclasses are responsible for the management of class resources, and they provide the "bed" for the instance of a class like you do with the constructor of a C++ class. For the first step we need to override three class methods, which begin with the letters wpcls. Each of them will be discussed separately. After you have declared the interfaces in your IDL file, you will run the SOM compiler SC against it. Enter SC OS2UG.IDL to compile the file. The SOM compiler generates an XH, an XIH, a CPP and a DEF file. Because it is common programming style to separate each function into a separate file, please do so accordingly. Unfortunately, the SOM compiler not only generates declarations, but also definitions within its header files; later you have to write a source file which #include's all of the separate files. Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.4. Overriding wpclsQueryStyle ═══ Overriding wpclsQueryStyle While developing WPS classes, you need to register and deregister them many times. You can avoid this by disabling the generation of a template of your WPS class. If you do not, you can only get rid of the template by rebooting into a command shell without the WPS running and delete the corresponding DLL's. With an additional reboot you can continue development. I disable the generation of a template to make life easier; this is achieved as follows (see the file m_qstyle.cpp): SOM_Scope ULONG SOMLINK wpclsQueryStyle(M_XWPS *somSelf) { /* M_XWPSData *somThis = M_XWPSGetData(somSelf); */ M_XWPSMethodDebug("M_XWPS","wpclsQueryStyle"); return (M_XWPS_parent_M_WPAbstract_wpclsQueryStyle(somSelf) | CLSSTYLE_NEVERTEMPLATE); // no templates } // END wpclsQueryStyle() It returns the parents style or'd with the style CLSSTYLE_NEVERTEMPLATE. That's all you need. The result is that now the template checkbox on the General page is not available. Thus you can deregister your class after deleting the instance and delete the DLL without the need to reboot your machine. Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.5. Overriding wpclsQueryTitle ═══ Overriding wpclsQueryTitle If you like to have a default title of your new class you will set it here (see the file m_qtitle.cpp). SOM_Scope PSZ SOMLINK wpclsQueryTitle(M_XWPS *somSelf) { /* M_XWPSData *somThis = M_XWPSGetData(somSelf); */ M_XWPSMethodDebug("M_XWPS","wpclsQueryTitle"); return (ClassTitle); } // END wpclsQueryTitle() Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.6. Overriding wpclsQueryDefaultView ═══ Overriding wpclsQueryDefaultView When you double click on the icon, which view should be opened? The settings notebook or the frame window? Our sample class opens a frame window (see the file m_qdefvw.cpp). This is done by declaring the value OPEN_XWPS1 (see the file os2ug.h) which is one of three possible frame windows: SOM_Scope ULONG SOMLINK wpclsQueryDefaultView(M_XWPS *somSelf) { /* M_XWPSData *somThis = M_XWPSGetData(somSelf); */ M_XWPSMethodDebug("M_XWPS","wpclsQueryDefaultView"); return (OPEN_XWPS1); }// END wpclsQueryDefaultView() Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.7. Overriding wpModifyPopupMenu ═══ Overriding wpModifyPopupMenu When you click with mouse button 2 (default right) on your application's icon, you would like to find a menu reflecting your default view. This will be added to the overridden function above (see the file modpupmu.cpp): SOM_Scope BOOL SOMLINK wpModifyPopupMenu(XWPS *somSelf, HWND hwndMenu, HWND hwndCnr, ULONG iPosition) { zString ModulePathName; // DLL Module path and name HMODULE hmod; // DLL MODULE handle /* XWPSData *somThis = XWPSGetData(somSelf); */ XWPSMethodDebug("XWPS","wpModifyPopupMenu"); // Get Module Name and Handle const IDynamicLinkLibrary dynid(cDllName); // dll name: OS2UG.DLL hmod = dynid.handle(); // Insert "Hello OS2UG" into Open Menu if (!somSelf->wpInsertPopupMenuItems( hwndMenu, 0, hmod, ID_OPENMENU, WPMENUID_OPEN)) { DebugMsg("Error in wpModifyPopupMenu()",ModulePathName ); } return (TRUE); } // END wpModifyPopupMenu() To retrieve the module handle of the DLL which contains the binaries for your popup menu I chose to use an instance of IDynamicLinkLibrary, a class of the UICL. The usual approach would be to call the SOM function somLocateClassFile(), but this doesn't work on my machine (your mileage may vary): ModulePathName = (*SOMClassMgrObject) .somLocateClassFile( SOM_IdFromString(ClassTitle), XWPS_MajorVersion, XWPS_MinorVersion); DosQueryModuleHandle( ModulePathName, &hmod); It would be a nice exercise for you to try both methods. Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.8. Overriding wpMenuItemSelected ═══ Overriding wpMenuItemSelected Now you have decided to open the "Hello OS2UG" view and the WPS processes the overridden method wpMenuItemSelected() (see the file muimsel.cpp). Here you will call wpViewObject() to open a view. This function calls your application's frame window routine: SOM_Scope BOOL SOMLINK wpMenuItemSelected(XWPS *somSelf, HWND hwndFrame, ULONG ulMenuId) { /* XWPSData *somThis = XWPSGetData(somSelf); */ XWPSMethodDebug("XWPS","wpMenuItemSelected"); switch( ulMenuId ) { case OPEN_XWPS1: somSelf->wpViewObject( hwndFrame, OPEN_XWPS1, 0); break; case OPEN_XWPS2: somSelf->wpViewObject( hwndFrame, OPEN_XWPS2, 0); break; case OPEN_XWPS3: somSelf->wpViewObject( hwndFrame, OPEN_XWPS3, 0); break; default: return (XWPS_parent_WPAbstract_wpMenuItemSelected (somSelf, hwndFrame, ulMenuId)); } return TRUE; } // END wpMenuItemSelected() Have you discovered that you can open three different views OPEN_XWPS1, OPEN_XWPS2 and OPEN_XWPS3? These start a frame window in either a separate thread, within the WPS thread of your notebook, or in a separate process, respectively. Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.9. Overriding wpOpen ═══ Overriding wpOpen Here you see the transition from the WPS world to your application's world, the "Hello WPS" frame window (see the file open.cpp). Here you call one of the CreateTUM_OS2_UGView*() methods, which are responsible for creating your frame window: SOM_Scope HWND SOMLINK wpOpen(XWPS *somSelf, HWND hwndCnr, ULONG ulView, ULONG param) { /* XWPSData *somThis = XWPSGetData(somSelf); */ XWPSMethodDebug("XWPS","wpOpen"); switch (ulView) { case OPEN_XWPS1: ptrIcon = somSelf->wpQueryIcon(); return CreateTUM_OS2_UGViewA(somSelf); break; case OPEN_XWPS2: ptrIcon = somSelf->wpQueryIcon(); return CreateTUM_OS2_UGViewB(somSelf); break; case OPEN_XWPS3: ptrIcon = somSelf->wpQueryIcon(); return CreateTUM_OS2_UGViewC(somSelf); break; } // end switch (ulView) return (XWPS_parent_WPAbstract_wpOpen (somSelf, hwndCnr, ulView, param)); } // END wpOpen() Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.10. Creating Your Frame Window ═══ Creating Your Frame Window If you're wondering why there hasn't been a lot of UICL usage, do not worry - to escape the WPS programming style a bit of work has to be done. But this was only a few lines of code; now you can start to fly. If you are ready to take off into the UICL heaven, let's begin. Within the function CreateTUM_OS2_UGViewB() you have the choice to create your frame window similar to the PM style (here in pseudo code, the actual code is in view_b.cpp): HWND CreateTUM_OS2_UGViewB(XWPS * somSelf){// view_b.cpp get the module handle; create the customized frame window; extract the frame window handle; somSelf->wpRegisterView(frame window handle, frame window title); return (frame window handle); }// end CreateTUM_OS2_UGView() Or you may create a separate thread for your application with the advantage of programming your application as usual in UICL. First, you create an EXE file. After finishing, you rename the main() function to something else, which is called by the thread; finally, you change some lines of code as illustrated below (in pseudo code, the actual code is in view_a.cpp and threadm.cpp). Please compare threadm.cpp with main.cpp: HWND CreateTUM_OS2_UGViewA(XWPS * somSelf){// view_a.cpp set up a semaphore; IThread myThread(threadMain, semaphore adress); wait until semaphore is posted but no longer than x seconds; obtain frame window handle; somSelf->wpRegisterView(frame window handle, frame window title ); return (frame window handle); }// end CreateTUM_OS2_UGView() void threadMain(void * arg) // threadm.cpp { obtain the handle to the binary of the resource create customized frame window; extract the frame window handle; post frame window handle; IApplication::run(); }// end threadMain() The third choice is to start your frame window as separate process. Here you have to use named semaphores. The pseudocode is identical with the threaded example with the exception that instead of a thread (see the file view_a.cpp) the process will be started by DosExecPgm() (see the files view_c.cpp and main.cpp). Here is where the second part of the project comes into play. Look back at the discussion of the wpOpen() method: you may create a frame window in another thread, the same thread, as well as in another process within the same sample application. The best way to illustrate the differences is to start the clock with the second-hand enabled. I have programmed the frame window with the choice of "bigjob" being either multithreaded or running within the same thread. Start "bigjob" and examine what happens with both the second-hand and the WPS when you try to select a different application! Now we will add some customized notebook pages to the WPS notebook. The page contains a "Welcome" bitmap and an MLE instance. [Note: the bitmap is a customized OS2LOGO.BMP, some of my friends got shocked seeing the "OS/2 Warp 2 Alpha" during start-up of my machine. It is only a joke; please do not send any email asking if I know any secrets about a new release of OS/2.] Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.11. Overriding wpAddSettingsPages ═══ Overriding wpAddSettingsPages Here you see an excerpt of adsetpgs.cpp. It is that simple to add additional pages to your WPS notebook. Is it really that simple? Yes, but you need a workaround for some strange behaviour which I noticed on my machine at least. If I would omit the IString instance the notebook would be reduced in size; thus none of the notebook pages are shown in entirety (see the note in the file adsetpgs.cpp). SOM_Scope BOOL SOMLINK wpAddSettingsPages(XWPS *somSelf, HWND hwndNotebook) { /* XWPSData *somThis = XWPSGetData(somSelf); */ XWPSMethodDebug("XWPS","wpAddSettingsPages"); Boolean rc; IString s1("workaround for strange behaviour. Keep this string her!"); // pages are added in reverse order, that is the last page first rc = somSelf->wpAddObjectGeneralPage(hwndNotebook); if (!rc) { DebugMsg("Error", "wpAddObjectGeneralPage()"); } // endif addMLENotebookPage( hwndNotebook ); rc = somSelf->wpAddObjectWindowPage(hwndNotebook); if (!rc) { DebugMsg("Error", "wpAddObjectWindowPage()"); } // endif addFirstNotebookPage( hwndNotebook); return (rc); } // END wpAddSettingsPages() By principle any function to add to a UICL notebook page looks the same. Let's have a look on the pseudo code: void addAnyNotebookPage( HWND * notebook) { prepare the notebook and the notebooks frame window handle; if a UICL frame window proxy doesn't exist create one; if a UICL notebook window proxy doesn't exist create one; prepare a notebook settings instance; add settings instance as first notebook page; attach a customized page settings handler to the notebook; } // END addAnyNotebookPage() The customized page handler takes care that the page is initialized only once, when the page is first shown. Only the page which will be shown will be instantiated. Thus the opening of a notebook is much quicker than you creating all page instances at once. I recommend this technique, because the user shouldn't be forced to wait too much. Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.12. Conclusion ═══ Conclusion So my friend, you have followed me to the end of the journey. I hope you will be able to create the killer application, fully integrated into the WPS with the help of the UICL. The UICL improves the programming of OS/2 allowing you to develop applications quicker and with less effort. Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 8.13. References ═══ References [1] Client/Server Programming with OS/2 2.1 by Robert Orfali and Dan Harkey, VNR / ISBN 0-442-01833-9 [2] OS/2 Warp Workplace Shell API by Mindy Pollak, Wiley / ISBN 0-471-03872-5 [3] OS/2 C++ Class Library Power GUI Programming with C-Set++ by Kevin Leong, William Law, Robert Love, Hiroshi Tsuji, and Bruce Olson, VNR ISBN 0-442-01795-2 Programming the WPS Using the UICL - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 9. 32-Bit I/O With Warp Speed ═══ ═══ 9.1. Introduction ═══ 32-Bit I/O With Warp Speed Written by Holger Veit Introduction Well, I have a little bit of a bad feeling. What I am going to describe in this article is something that should not be possible at the level of user application programs: direct I/O. This is the ability of a user program to talk to hardware resources directly, not controlled or even monitored by the operating system. Although it might have been common practice in a simple program loader like DOS, it is normally not a good idea in a modern multitasking or multi-user system. In fact, OS/2, Windows NT, and the various UNIX derived operating systems, spend considerable efforts to hide this practice from user applications, or explicitly prohibit it. The reasons for that are obvious and well known:  User programs could access system resources, that means here hardware devices, in an uncoordinated or uncooperative way, and could therefore severely influence or even damage data integrity, system stability, and security.  While the operating system tries to distribute the limited resources in a fair way among the various competitors, a program that circumvents the care of the operating system, could grab such a resource exclusively. Even worse, it might struggle with the operating system for control, which will unlikely have good consequences on the overall system throughput.  Needless to say, a malign process like a computer virus could use a feature like direct I/O to intentionally destroy data. However, even with good intention, a user program is not immune against software bugs that might not only crash the program itself but also take the system with it into the abyss. On the other hand, people usually do not want to have I/O access to write improved support for complicated disk or video devices. Rather they have simple I/O devices like data acquisition cards, parallel I/O boards, EPROM programmer adapters or alike, that do not come with OS/2 support at all. Writing a full-blown OS/2 device driver in order to reuse an old 8255 port card seems like shooting sparrows with cannons. In such a case, where neither interrupt handling or DMA, nor physical access to adapter memory is involved, direct I/O appears to be a feasible alternative. In the following chapter, we will look at the official methods that OS/2 offers to do I/O. 32-Bit I/O With Warp Speed - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 9.2. Doing Port I/O: The Front Doors ═══ Doing Port I/O: The Front Doors Device drivers No doubt, as was already stated, a device driver is the real solution. Keep critical tasks away from the user is the rule of the game. The path to this is quite stony, however. As often criticized, device drivers in OS/2 are still 16 bit code. This has several disadvantages.  You need to code entirely in assembler, or find a C compiler that still produces 16 bit code. Most of the standard drivers that come with OS/2 were built with either MASM 5.1 or MSC 6.0. It is not only the problem that IBM relies on products of a competitor here, but simply that these products are no longer available in stores, and the successors no longer support OS/2. Fortunately, the recent, widely available Watcom 10.0 compiler is usable for device driver development.  Coding in 16 bit throws the developer back into the stone age where he has to fight against 64K segments, far and near calls, moving around selectors and grouping segments in a certain order. Nevertheless, one could think about writing a device driver that you can use to read and write I/O ports by issuing special DosDevIOCtl instructions. Surprisingly, this is an unnecessary enterprise! Not many OS/2 users know that every stock OS/2 system comes with a device driver that comes with such a functionality: TESTCFG.SYS. Besides some other functions that are beyond the scope of this article, TESTCFG.SYS offers two ioctls, one for reading I/O ports and another one for writing. See table 1 for the description of the functions and the simple program fragment in figure 1 for an example. Device name "TESTCFG$" Description Read data from I/O port IOCTL Category 0x80 IOCTL Function 0x41 Parameter packet struct { USHORT portaddr; USHORT size; } param Data packet struct { ULONG dataread; } data; Remarks size=1: read 8-bit size=2: read 16-bit size=3: read 32-bit Description Write data to I/O port IOCTL Category 0x80 IOCTL Function 0x42 Parameter packet struct { USHORT portaddr; USHORT size; ULONG datawrite; } param Data packet none Remarks size parameter same as for function 0x42 param packet in "Writing OS/2 2.1 Device Drivers in C" book is wrong! Table 1: Ioctl API of TESTCFG.SYS for doing direct I/O /* direct I/O with TESTCFG.SYS */ #define INCL_DOSFILEMGR #define INCL_DOSDEVIOCTL #include HFILE fd; ULONG action, len; APIRET rc; struct { USHORT port; USHORT size; } par; struct { ULONG data; } dta; rc = DosOpen("/dev/testcfg$", &fd, &action, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE, (PEAOP2)NULL); /* check error code.... */ par.port = 0x84; /* use a mostly harmless port */ par.size = 1; /* read byte */ rc = DosDevIOCtl(fd, 0x80, 0x41, (PULONG)&par, sizeof(par), &len, (PULONG)&dta, sizeof(dta), &len); /* check error code.... */ printf("Data was 0x%lx\n", dta.data); rc = DosClose(fd); /* needless to say: check.... */ Figure 1: Sample code to read a port through TESTCFG.SYS There is one drawback, and we will hear this argument again real soon: it is slow. Why? Now see, we are calling this function from a 32 bit user program. The DosDevIOCtl() will enter the kernel through a call gate which is some kind of a protected door. The kernel will then first check the validity of the parameter and data packets, identify the target driver to perform this function, and then call the appropriate driver entry point. Note the driver is 16 bit code, so the kernel must convert the addresses of the parameter and data packets from 0:32 bit user space addresses to 16:16 bit device driver addresses. Finally, the driver itself must decode the command and dispatch it to its routines. I once tried to trace such an ioctl call with the kernel debugger, and eventually gave up counting after following some hundred instructions without seeing any driver code. Compare this with a single IN or OUT instruction. That's bureaucracy! IOPL Segments The second method is actually a leftover from earlier OS/2 1.X versions, hence it is a 16 bit technique as well. Let me elaborate here a bit on the method used to prevent I/O access by user programs. The Intel 286 and later (386, 486, Pentium) processors can execute code at four different privilege levels. Because they are nested and usually drawn as concentric circles, these levels are frequently referred to as privilege rings (or protection rings). Ring 0 is the level with the highest privilege, and ring 3 has the lowest privilege (see figure 2). Figure 2: The privilege rings and their use in OS/2 If a process wants to run with a higher privilege than the one it currently has, it must go through a special gate; one might also compare a gate with a tunnel or "wormhole". There are several types of gates such as interrupt, trap, or task gates. The only interesting type for us is the call gate. A call gate allows a one-way transfer of execution from a segment with some privilege to another one with same or higher privilege. The other direction, that is from a "trustworthy" high-privileged code segment to a less trusted lower-privileged segment, is not possible. See figure 2. Figure 3: Allowed and forbidden transactions with call gates Two bits in the processor status register (the IOPL field) determine the level that is necessary to execute I/O CPU instructions. Any code with less than this privilege level will trigger an exception at the first attempt to execute such an instruction. Table 2 lists the affected instructions. Certain instructions will even cause an exception if the process has the privilege to I/O. These instructions require ring 0 privilege. Table 2 also lists these instructions (386 processor). ┌────────────────────────────────────────┬────────────────────────────────────────┐ │Instruction is protected and causes an │Affected instructions │ │exception... │ │ ├────────────────────────────────────────┼────────────────────────────────────────┤ │if not in Ring 0 │LIDT, LGDT, LMSW, CLTS, HLT MOV DR*, reg│ │ │/ MOV reg, DR* MOV TR*, reg / MOV reg, │ │ │TR* MOV CR*, reg / MOV reg, CR* │ ├────────────────────────────────────────┼────────────────────────────────────────┤ │if in Real Mode or in Virtual 8086 Mode │All the above plus LTR, STR, LLDT, SLDT,│ │ │LAR, LSL, VERR, VERW ARPL │ ├────────────────────────────────────────┼────────────────────────────────────────┤ │if not at IOPL privilege level or higher│All ring 0 instructions plus OUT, INS, │ │(in Protected Mode) │OUTS, REP INS, REP OUTS STI, CLI │ ├────────────────────────────────────────┼────────────────────────────────────────┤ │if not at IOPL privilege level or higher│All ring 0 and Virtual 8086 Mode │ │(in Virtual 8086 Mode) │instructions plus INT n (Note 1) PUSHF, │ │ │POPF, STI, CLI, IRET (Note 2) │ └────────────────────────────────────────┴────────────────────────────────────────┘ Note 1: INT 3 (opcode 0xcc) and INTO are not affected Note 2: I/O instructions are enabled or disabled by the I/O permission map in the 386 task state segment Table 2: Privileged Instructions In OS/2, the required privilege level for I/O is ring 2 or better, and tough luck, any user process only runs in ring 3 (figure 2). In order to get a controlled way to do I/O, the OS/2 developers provided a method to execute 16 bit code at ring 2 level. When the linker produces an executable from several object files, it accepts a special attribute for code segments under certain circumstances. This attribute is named IOPL and is specified in the segment declaration section of a linker definition file (Consult appropriate linker documentation). The linker then annotates the code in a way that every call of a routine in this IOPL segment will be directed through a call gate, rather than a simple call. When such a program is loaded into memory for execution, the loader code in the kernel will generate a R3->R2 call gate for each target called in an IOPL segment (see call gate X in figure 3). Each time such a call gate is entered, the processor will gain ring 2 privilege and lose it again when leaving by a normal return instruction. Apparently, this looked like a feature which could be abused, so the IBM developers restricted it in a way that only segments in a DLL can get the IOPL attribute. This appears to be a built-in feature of the program loader, not just the linker, as patching the appropriate tables in the executable will not work. This restriction is not a bad idea, as it is now no longer possible to make an executable disguising as a normal program, but doing I/O inside. There must be an accompanying DLL, to arouse suspicion - or at least should do so. This could have been an almost ideal way for moderate I/O - if IBM had provided a similar method for 32 bit applications as well. There is no restriction in the processor itself concerning 32 bit I/O, as one might suspect; it is an intentional limitation. Since IBM will not support 16 bit software any longer in OS/2 for the PowerPC, those unsecure interfaces will disappear in the future. Nevertheless, you can call routines in such a 16 bit IOPL DLL from a 32 bit executable, and there are several example files floating around in various FTP archives. The key item here is thunking. The main problem with calling code of another size gender is that the program counter as well as the stack pointer needs to be adjusted to the corresponding other size. If address parameters are passed through the stack, these addresses need to be converted as well. This is what a thunking routine does. Usually the compiler generates such routines automatically when a 16 bit routine is declared, and this is why many high- level programmers do not encounter them at all. However, even if they seem to be invisible, they nevertheless contribute a considerable share to performance degradation if an I/O routine in the IOPL DLL is called from a 32 bit application. 32-Bit I/O With Warp Speed - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 9.3. Doing I/O: Searching for Mouse Holes ═══ Doing I/O: Searching for Mouse Holes In the last section, we have seen that it is possible to do direct I/O with the already available facilities. The difficulty is just the excessive overhead that makes their use quite unattractive, and with the text of the preface still in mind, there can be no doubt that this is not incidental. However, as some of you might know about my ambitious pet project, it was indispensable for me to find an extremely fast alternative to the above stuff. Although I prefer writing a device driver for that kind of applications, it seemed entirely impossible to put a complete Xserver into a 16 bit device driver (that beast, with PEX, is as large as 2 MB - 32 bit code!). Moving only the critical parts into a driver might work; unfortunately the XFree86 people are too creative for me, so it would be expectable that I'd be hurrying to get their recent changes integrated for the rest of my life. So let us discuss possible alternatives. Outwitting the Program Loader? As we have seen in the discussion of the IOPL mechanism, the bottleneck is the thunking code. Interestingly, there exist types of call gates that can mediate among 32 and 16 bit code and do the necessary conversion of the program counter and stack pointer automatically. Unfortunately, the program loader refuses to make them for us. Likewise, there seems to be no chance to have it create a 32->32 bit call gate. A brute force approach could be trying to identify the call gates it made for us and redirect them to the routines that we want to run with privilege. Since we need certain instructions to manipulate the GDT or LDT (more on that later), this is not possible from a user program, because the kernel protects these structures well in a ring 0 segment. Similar to the restriction not to pass a call gate in the wrong direction, a process cannot read, let alone write, data of a higher ring level. This is not a real problem, if we have an accomplice with sufficient rights to do the dirty work for us: a device driver. However, besides being a bad hack, such a solution is still half-hearted. Once we find a method to manipulate call gates, we no longer need to have this code separated in a DLL, as with the IOPL anachronism, but we could keep it in the executable itself. Furthermore, while we are on this way, couldn't we just manipulate the code segment of the user process into a ring 0 one? Let us think about this possibility. User Processes at Ring 0? During reading, you might have thought about the question how the processor knows which privilege the currently executed code has, and where it keeps this information. This is pretty simple in protected mode: somewhere in memory there are two tables that describe the location, size and properties of each memory segment. They are called global descriptor table (GDT) and local descriptor table (LDT). While the GDT describes system wide structures, there is usually an LDT for each process in the system. Two special registers of the CPU, GDTR and LDTR, point to the beginning of the tables. Each table is an array with elements of eight bytes in size. The index into these arrays is fairly simple: it is formed from the upper 13 bits of the 16 bit segment registers (CS, DS, ES, SS, FS, GS). Bit 2 of these registers distinguishes between LDT and GDT, and the two lowest bits describe the current privilege the CPU is running. What is most important is that any segment descriptor in the tables also contains two bits that determine the privilege level the code runs under. So the way to go seems clear: get the content of the CS register, find the corresponding GDT or LDT entry, and switch the privilege bits to ring 0. This is possible. However, this game will likely end very fast with a trap and the register display on the text mode screen. Why? We have seen in figure 3 that privileged ring 0 code will never execute less privileged code. However, certain system or application DLLs required by the user program still have ring 3 level. So the unfortunate consequence is: if your user program has privileges, it will lose the ability to call several system functions. The immediately upcoming flash of an idea of promoting the system and user DLLs to the same level as well, is hopefully not meant seriously, as it will end up with all software running privileged. It therefore appears that any trick to raise the privilege of the user process introduces more problems rather than solving them. Let us try to approach the problem from a totally different side. Maybe we could reduce the overhead of performing functions in a device driver. A Back Door into a Driver: DevHlp_DynamicAPI Time to dive a bit deeper into a device driver. The kernel provides a set of routines known as device helper functions to a device driver. One of these helpers appears particularly attractive, as it promises to create a ring 0 gate directly into a device driver. So the idea is to build the I/O functions into the driver and create such a dynamic API entry point. This will return a GDT selector that a user process can enter with an indirect intersegment call instruction. Estimating the overhead, this should be considerably faster than the bureaucratic way of TESTCFG.SYS. At least that's how it works in theory. In fact, I programmed it this way, and it worked, but it did not even reach the slow speed of TESTCFG.SYS. Careful single stepping showed the following: the DevHlp_DynamicAPI created the call gate, but the gate did not point straight to the driver routine I wrote for the I/O access. Instead, it pointed to somewhere in the kernel, into a routine DYNAM_API_4. This entry point then performed almost all the fiddling I observed earlier when tracing the ioctl of TESTCFG.SYS. What was even worse was what the "4" in the label of the first routine told me. I had broached a scarce resource. Analysis showed that there are only 16 of these entry points available system wide, and mine was already the fifth one in use. I have not the slight idea about the other four clients, but it does not seem to be a good idea to deliberately use up one of those expensive and rare interfaces. But in principle, the idea was correct. 32-Bit I/O With Warp Speed - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 9.4. /DEV/FASTIO$ - the Final Way ═══ /DEV/FASTIO$ - the Final Way Okay. We just managed to get a transforming (32->16bit) call gate, that just happens to point to the wrong address. It was a matter of seconds to find the address of the corresponding GDT entry, and redirect it to the expected position. A kernel debugger is really a neat tool for the hacker. It worked! At this point, calling the DevHlp_DynamicAPI function becomes useless, and will just occupy a later unusable entry point in the kernel. A quick look into the list of device helper functions offers the function DevHlp_AllocGDTSelector. We acquire a default GDT selector for exclusive use by the driver, and "adjust" it to form a 32->16 bit R3->R0 call gate into the I/O routine section of the driver. Have a look at the code fragment in the FASTIO$ driver (figure 4) which does it all. .386p _acquire_gdt proc far pusha mov ax, word ptr [_io_gdt32] ; get selector or ax,ax jnz aexit ; if we didn't have one ; make one xor ax, ax mov word ptr [_io_gdt32], ax ; clear gdt save mov word ptr [gdthelper], ax ; helper push ds pop es ; ES:DI = addr of mov di, offset _io_gdt32 ; _io_gdt32 mov cx, 2 ; two selectors mov dl, DevHlp_AllocGDTSelector ; get GDT selectors call [_Device_Help] jc aexit ; exit if failed sgdt qword ptr [gdtsave] ; access the GDT ptr mov ebx, dword ptr [gdtsave+2] ; get lin addr of GDT movzx eax, word ptr [_io_gdt32] ; build offset into table and eax, 0fffffff8h ; mask away DPL add ebx, eax ; build address in EBX mov ax, word ptr [gdthelper] ; selector to map GDT at mov ecx, 08h ; a single entry (8 bytes) mov dl, DevHlp_LinToGDTSelector call [_Device_Help] jc aexit0 ; if failed exit mov ax, word ptr [gdthelper] mov es, ax ; build address to GDT xor bx, bx mov word ptr es:[bx], offset _io_call ; fix address off mov word ptr es:[bx+2], cs ; fix address sel mov word ptr es:[bx+4], 0ec00h ; a r0 386 call gate mov word ptr es:[bx+6], 0000h ; high offset mov dl, DevHlp_FreeGDTSelector ; free gdthelper call [_Device_Help] jnc short aexit aexit0: xor ax,ax ; clear selector mov word ptr [_io_gdt32], ax aexit: popa ; restore all registers mov ax, word ptr [_io_gdt32] ret _acquire_gdt endp Figure 4: Initialization routine of FASTIO$ driver Since a device driver is initialized in ring 3, this routine does not work during startup. Rather, the driver will call this code once the first time some client opens the device. Thus, to use the driver, a small routine io_init() needs to be called first. Refer to the file iolib.asm that comes with this issue of EDM/2. A final improvement: Usually, C code passes arguments on the stack. A call gate can be configured to copy these parameters over to the new ring. But why should we do this? For really fast I/O access we pass the data in registers. This allows for direct replacement of I/O instructions in assembler code by a simple indirect call as shown in figure 5. The address of the indirect call is set up by the above mentioned io_init() procedure. EXTRN ioentry:FWORD : MOV DX, portaddr MOV AL, 123 MOV BX, 4 ; function code 4 = write byte CALL FWORD PTR [ioentry] : Figure 5: Calling I/O from assembler If the code needs to be called from C, we simply write a small stub that wraps a stack frame envelope around it, just as shown in figure 6. ; Calling convention: ; void c_outb(short port,char data) ; ; PUBLIC _c_outb PUBLIC c_outb _c_outb PROC c_outb: PUSH EBP MOV EBP, ESP ; set standard stack frame PUSH EBX ; save register MOV DX, WORD PTR [EBP+8] ; get port MOV AL, BYTE PTR [EBP+12] ; get data MOV BX, 4 ; function code 4 = write byte CALL FWORD PTR [ioentry] ; call intersegment indirect 16:32 POP EBX ; restore bx POP EBP ; return RET ALIGN 4 _c_outb ENDP Figure 6: A C callable I/O function The file iolib.asm contains a set of functions c_inX() and c_outX() for using I/O from any 32 bit compiler that supports the standard stack frame. The files iolib.a and iolib.lib are precompiled versions; the file iolib.h contains the C prototypes. In the complete driver, I gave up a small amount of the theoretically reachable performance. There are six basic I/O operations: IN and OUT instructions exist for transferring bytes, 16 bit words and 32 bit long words. To become really fast, one would have to provide a separate GDT selector for each of them. In a typical OS/2 system, this should not be a problem. However, if now everyone would start to add more routines, each with its own entry point, this resource could become rather quickly a scarce one. So I spent a function code, to be passed in the BX register, to multiplex the six functions into a single GDT selector. Refer to the io_call entry point in the fastio_a.asm driver source file. 32-Bit I/O With Warp Speed - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 9.5. Conclusion ═══ Conclusion The article demonstrated how a specialized device driver was used to assist a user process in performing direct I/O. The final overhead, compared to a pure device driver or a DOS program implementation, is just the CPU cycles of the indirect intersegment call through the call gate and the return instruction. Every other available method significantly adds a performance penalty. This also holds for I/O in a DOS Box, which was not explained in this article. It is to be expected, however, that this method will not be available any longer in future Power PC systems, so avoid the demonstrated trick unless absolutely necessary. 32-Bit I/O With Warp Speed - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 10. /dev/EDM/BookReview ═══ ═══ 10.1. Introduction ═══ /dev/EDM2/BookReview Written by Carsten Whimster Introduction In /dev/EDM2/BookReview, I focus on development books and materials. The column is written from the point of view of an intermediate PM C programmer and intermediate REXX programmer. Pick up whichever book strikes your fancy, and join the growing group of people following our PM programming columns. I have already reviewed a number of beginner's books, and will try to concentrate a bit more on intermediate techniques and special topics from now on. Please send me your comments and thoughts so that I can make this column as good as possible. I read and respond to all mail. Programming the OS/2 Warp GPI is the most up-to-date book specifically covering the GPI, which I have reviewed yet. /dev/EDM2/BookReview - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 10.2. Errata ═══ Errata My web page is finally coming of age, despite a minor accident. The OS/2 section has grown dramatically, and now has a large number of useful links to other OS/2 sites. Check it out at http://www.undergrad.math.uwaterloo.ca/~bcrwhims/. My machine has been upgraded to an AMD DX4-100 with 20 Mb RAM and an ATI Mach64 with 2 Mb DRAM. Due to the increased speed and responsiveness, my interest in developing is growing once again. I am working on an update to my POV-Panel/2, and also on a couple of other projects, including a POV-Ray editor, and a PM version of an old video game called Qix. We'll see how far I get with all of it though . /dev/EDM2/BookReview - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 10.3. Programming the OS/2 Warp GPI ═══ Programming the OS/2 Warp GPI For a while now, the OS/2 world has been without an in-depth, up-to-date book on the GPI. Now things are about to change, both because of this book, and also because Graham Winn's book has been updated as well. Here are the chapter headings for this, the newest of Stephen Knight's and Jeffrey Ryan's OS/2 books:  Overview  OS/2 Architecture  Graphic Primitives  Fonts  Building Blocks of the GPI  Transformations  Paths, Regions, Clipping, Boundary Accumulation, and Correlation  Printing Graphics  OS/2 Metafiles Appendix A: GPI Functions Appendix B: Working with the Diskette This is a book by the author of Learning to Program OS/2 2.0 Presentation Manager by Example, and it shows. I criticized that particular book for only including a single, monolithic program as the example the whole book was built around. This book has the same basic structure, but I won't reiterate my criticism unless I feel that this book also suffers from the same faults that I described in the first one. Reading this book quickly showed that the authors' ability to explain difficult material has not decreased with time. The writing is lucid, and more in-depth than the first book. This book is quite unusual in that it presents a complete, well-featured program in good detail throughout the chapters. The Overview presents the graphics application from the user's perspective, and really is a user manual, albeit a little short. The next chapter, inexplicably called OS/2 Architecture, spends most of its time explaining the architecture of the GPI, and the OS/2 graphics viewing pipeline. The GPI architecture is quite complicated, and not really intuitive at all, but when you spend some time reading up on it, a lot of it come together. I still wonder if there isn't a better way to do things, though. It really does seem needlessly complicated to me. Hopefully DIVE will give those of us who prefer building things more manually a much-needed reprieve from the GPI. Anyway, enough complaining about the GPI! The third chapter discusses graphic primitives like lines, circles, ovals, rectangles, as well as graphic attributes like line width, line colour, character colour, character shear, image colour, marker mix, and so on ad infinitum. There really are an absolutely astounding number of attributes to worry about, but they can be grouped into logical subsets, of course. These subsets are then explained briefly, followed by explanations of the line and arc primitives, including line join options. Some of the specific line options are outlined, but the main philosophy of the book is to explain only the most common functions in detail. Others may or may not get mentioned, but you can rest assured that 90-95% of what you will use is here. Some of the fills are also explained here, including the functions that use them. Also in this chapter is an introduction to text primitives, or in other words, using fonts in graphics. Finally, we are treated to an introduction to bitmap usage. Chapter four is the first non-introductory chapter on any subject so far, and it attacks fonts. By the way, if you look at the chapter headings of this book, and compare them to the chapter headings of Winn's book, you will see a remarkable similarity there. I suppose it is unavoidable to a certain extent, but it doesn't look like a complete coincidence. Indeed, Graham Winn is given credit in the Acknowledgements section, so it looks like there is a fairly close relationship between the various graphics-book authors working at IBM. It should be interesting to see what Winn's new book will look like. In any case, back to the Fonts chapter. This chapter, like the rest, has an introductory paragraph giving a little background to the subject. I prefer to see a little more, and to have it also give a quick preview of what the chapter contains so that you know where you are headed, but that could be just personal preference. After the introductory paragraph, we are told a bit about codepages. This is interesting material, but at the same time, I really wish it didn't have to be there, ie. IBM should take some initiative with OS/2, and move towards double-byte characters, so that we won't have to deal with codepages any more. Yes, this would slow down our systems a bit, but I think that in the long run, it is worth it. But, back to our book. Outline fonts, image (bitmapped) fonts, kerning, font metrics, font attributes, and width tables are all introduced, and explained, and some larger code snippets out of the sample application are given. The fifth chapter launches into an explanation of the building blocks that the GPI is constructed from, such as graphics orders and elements. Segments are covered in good depth, and then the graphics viewer is introduced into our subject matter. Quite a bit of code is given along the way, in large-ish chunks. Sometimes it is a little hard to grasp everything, with so much listed at a time. The next chapter deals with the various transformations that the GPI allows you to perform on objects, including revisiting the coordinate spaces. You generally draw your objects first, and then put them through the transformations on the way to their final form. A fair amount of matrix math is needed to do this, so be sure that you are up on this subject before attempting to read this chapter. Of course, the GPI does supply the tools to do this, but comprehension is important. Paths, regions, and so on is chapter seven's subject. It deals with the possibility of creating paths and regions, and then using these to either clip, fill, or otherwise modify various (other) objects. This is a fairly powerful technique, which you will most likely find a need for if you get into GPI in any depth at all. There are various ways to define paths and regions, and ways of combining them. Redrawing using regions is also explained. Model space clipping, page space clipping, device space clipping, and other clipping related options are all sub-sections of this chapter. In addition to these, there is a section called correlation, which is a bit of a god-send to anyone trying to design something like a Doom editor. This subject deals with the GPI's ability to select objects based on mouse- clicks and keystrokes. To this end, tags are explained here. They are used with objects to yield the desired granularity for the selection operation. A medium-sized code snippet goes along with this, but my feeling is that some more depth would have been nice here. Printing graphics is the next chapter, and it explains the device independence of OS/2's printing sub-system, as well as a basic outline of the graphics-printing relationship in OS/2. Chapter 9 delves into OS/2's metafiles, and the raison-d'etre for this otherwise fairly unused format. Basically, it is meant as a go-along to the GPI, and is supposed to be used for picture interchange between applications of different nature. It saves more information than the other non-native format of the drawing editor, TIFF, in that drawing commands themselves can be stored in a metafile. It is in some ways more similar to a printer or the screen, than to other graphics formats, in that it is handled as a device under OS/2. Again, a large- ish chunk of code is regurgitated at the end of the chapter, making the reading a little heavier than usual. With these large code-snippets, it is harder to dissect what is necessary, and what is frills. Still, a good deal more can potentially be taught as opposed to small programs that do little. The appendices have information on the GPI functions (basically a quick-reference listing of the GPI functions of Warp), and a small section on how to use the accompanying diskette. /dev/EDM2/BookReview - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 10.4. Summary ═══ Summary I like this book a fair amount, and it is a definite improvement on the feel of Stephen Knight's Learning to Program OS/2 2.0 Presentation Manager by Example. At the same time, having only a single sample program has to have its limitations, and I can't help but feel that certain areas could have been covered more in depth. This does not in any way mean that this book is not a good book, and in fact, one might equally well complain that most books, by having a large number of small programs miss out on the chance to treat certain other subjects in depth. It is more of a new angle than a bad angle. I personally prefer more small programs, but I won't let this affect my judgement of the book, because not everyone feels the same way. I feel that sometimes the reader is left for a long time without concrete code to browse, and other times the code snippets get a little large to digest. For this reason only, I give this book an A-, rather than an A. /dev/EDM2/BookReview - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 10.5. Book Reviewed ═══ Book Reviewed  Programming the OS/2 Warp GPI, Knight and Ryan. - Wiley and Sons. ISBN 0-471-10718-2. US$39.95, CAN$55.95. - OS/2 Graphics Programmers - A- This book should be extremely attractive to anyone writing a graphics editor, since it includes a capable, if not full-featured graphics editor program and full source. It may appeal less to games writers and other graphics programmers due to the limited nature of using only one sample application. Very readable, and well-explained, however. NOTES Please note that books aimed at different audiences should only be compared with great care, if at all. I intend to concentrate on the strong points of the books I review, but I will point out any weaknesses in a constructive manner. LEGEND BOOK: The name of the book, and the author(s) PUBLISHING INFORMATION: Publishing company, ISBN, and approximate price. AUDIENCE: This is a description of the audience I think the book targets best. This is not intended as gospel, just a guideline for people not familiar with the book. MARK: My opinion of the success of the book's presentation, and how well it targets its audience. Technical content, accuracy, organization, readability, and quality of index all weigh heavily here, but the single most important item is how well the book covers what it says it covers. Many books try to cover too much, and get a lower mark as a result. A+ Ground-breaking, all-around outstanding book. A Excellent book. This is what I want to see happen a lot. A- Excellent book with minor flaws. B+ Very good book with minor flaws or omissions. B Good book with some flaws and omissions. B- Good book, but in need of improvement. C+ Mediocre book with some potential, but in need of some updating. C Mediocre book with some good sections, but badly in need of fixing. C- Mediocre book, little good material, desperately in need of an overhaul. D Don't buy this book unless you need it, and nothing else exists. F Don't buy this book. Period. COMMENTS: This is a very brief summary of the review proper. /dev/EDM2/BookReview - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 10.6. Index ═══ Index This Content Index is designed to let you find the book that covers the topics you need to learn about. It will eventually have a lot of categories, with each book being rated along each row. These tables will be quite large, and will continually grow, so please give me your feedback regarding what categories you would like to see, and which you don't. It may take me a while to flesh them out, so have a little patience. BOOK LEGEND ┌─────┬───────┬───────────────────────────────────────────────────────────────────┐ │Code │Issue │Title │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │RWP │2-3 │Real World Programming for OS/2 2.1 │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │LPE │2-4 │Learning to Program OS/2 2.0 Presentation Manager by Example │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │ODD │2-5 │Writing OS/2 2.1 Device Drivers in C │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │GPI │2-6 │OS/2 Presentation Manager GPI │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │TAO │2-7 │The Art of OS/2 2.1 C Programming │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │MOR │2-8 │Mastering OS/2 REXX │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │RSH │2-9 │REXX Reference Summary Handbook │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │ADO │2-10 │Application Development Using OS/2 REXX │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │PMP │2-11 │OS/2 Presentation Manager Programming │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │DOA │3-1 │Designing OS/2 Applications │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │OSP │3-2 │OS/2 Programming │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │TGO │3-4 │The GUI-OOUI War │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │OU │3-5 │OS/2 Warp Unleashed, Deluxe Edition │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │QR1 │3-6 │WIN Functions, OS/2 Quick Reference Library Volume 1 │ ├─────┼───────┼───────────────────────────────────────────────────────────────────┤ │PWG │3-7 │Programming the OS/2 Warp Version 3 GPI │ └─────┴───────┴───────────────────────────────────────────────────────────────────┘ NOTE: Books which cover the same material can look similar in this table, but be different in real life. The style of a book, for example, can not be seen from a quick table, so make sure that you follow up by reading the reviews of the books you find here. Finally, be sure that the books you are comparing are aimed at the same audiences. PM C BOOKS ┌─────┬─────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┐ │BOOK │MARK │Kernel │Device │VIO and│PM │GPI │Fonts │Print │ │ │ │Basics │Driver │AVIO │Intro │ │ │ │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │RWP │B+ │2 │0 │0 │4 │4 │4 │3 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │PME │B- │1 │0 │0 │2 │2 │2 │0 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │ODD │A │0 │5 │0 │0 │1 │0 │1 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │GPI │C+ │0 │0 │0 │0 │5 │2 │3 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │TAO │B+ │3 │2 │1 │4 │1 │2 │0 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │PMP │A- │1 │0 │1 │5 │3 │4 │2 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │OSP │B+ │2 │0 │0 │3 │2 │1 │0 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │QR1 │A │0 │0 │0 │(WIN) 5│0 │0 │0 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │PWG │A- │0 │0 │0 │1 │5 │5 │5 │ └─────┴─────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┘ REXX BOOKS: ┌─────┬─────┬─────────┬─────────┬─────────┐ │BOOK │MARK │REXX │WPS │Reference│ │ │ │Intro │ │ │ ├─────┼─────┼─────────┼─────────┼─────────┤ │MOR │B │4 │0 │2 │ ├─────┼─────┼─────────┼─────────┼─────────┤ │RSH │A │1 │2 │5 │ ├─────┼─────┼─────────┼─────────┼─────────┤ │ADO │A- │3 │2 │4 │ └─────┴─────┴─────────┴─────────┴─────────┘ SYSTEM AND NON-PROGRAMMING BOOKS: ┌─────┬─────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┐ │BOOK │MARK │Kernel │Device │VIO and│PM │Thread │GPI │Fonts │Print │WPS │ │ │ │Basics │Driver │AVIO │ │ │ │ │ │ │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │DOA │A │4 │4 │2 │4 │5 │3 │2 │3 │0 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │TGO │B │0 │0 │0 │2 │1 │0 │2 │1 │5 │ ├─────┼─────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┼───────┤ │OU │A+ │1 │4 │4 │5 │2 │5 │5 │5 │5 │ └─────┴─────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┘ RATINGS LEGEND: ┌─┬──────────────────────┐ │0│No coverage │ ├─┼──────────────────────┤ │1│Very light coverage │ ├─┼──────────────────────┤ │2│Introductory coverage │ ├─┼──────────────────────┤ │3│Good Coverage │ ├─┼──────────────────────┤ │4│In-depth coverage │ ├─┼──────────────────────┤ │5│Authoritative │ └─┴──────────────────────┘ /dev/EDM2/BookReview - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 10.7. Coming Up ═══ Coming Up I am expecting Reich's new book for next month, but we'll see if it gets here in time. The following are some other books I intend to review, in no particular order:  Designing High Powered OS/2 Applications, Reich  OS/2 Presentation Manager GPI, 2nd edition, Winn  The Design of OS/2, 2nd Edition, Kogan and Deitel If anyone has a book they want to see reviewed, I will be happy to oblige. Just mail me and tell me. Publishers can send me books at the address on my personal page at the end of the magazine, and I will review all OS/2 development- related and advanced user books I receive. /dev/EDM2/BookReview - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 11. OOPS Avenue ═══ ═══ 11.1. Introduction ═══ OOPS Avenue Written by Gordon Zeglinski Introduction There is a new C++ library out called STL (Standardized Template Library). As the name implies, the library is being standardized and consists of template classes and functions. There are a couple of versions of STL available. The easiest (and cheapest) one to get is the HP source code available from butler.hpl.external.hp.com in the /stl directory. So now that you know where to get it, let's take a brief look at it. OOPS Avenue - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 11.2. What Is STL? ═══ What Is STL? STL is a set of template functions and classes that provide a portable compiler neutral toolkit for manipulating and storing data. Roughly speaking, STL does for "container objects" what the IOSTREAM library does for I/O. The two implementations of STL that I've looked at were not header file compatible. Thus, one should consider STL still in its infancy. The design of STL is very different from most other container type object classes. While most use templates, they also use inheritance and virtual functions. This both affects the performance and the ability to share the object across processes. Virtual functions are usually implemented as lookup tables attached to the object's instance; when a virtual function is called, its address is pulled from the lookup table. This address is then used to call the appropriate member function. If, for example, an instance of an object is created in shared memory, and this object has virtual functions, the lookup table would be filled with addresses of functions belonging to the process that created the instance. This of course means that only the process creating this instance can use this instance. Because STL doesn't use virtual functions, this limitation is not present. Another interesting point about the design of STL is that functions to sort, search or otherwise manipulate data are not member functions of the container classes. This allows the same functions to operate on C-style arrays and C++ based container classes. The class can be divided into 5 primary components: algorithms, containers, iterators, function objects and adaptors. Algorithms perform actions such as sorting, searching, merging and the like. Containers manipulate groups of objects and the memory they require. Iterators are used to move through a container. Function objects encapsulate functions use by other parts of the library. Adaptors provide an alternative interface to a component of the library. OOPS Avenue - EDM/2 - Aug 1995 - Volume 3, Issue 7 ═══ 11.3. A Simple Program ═══ A Simple Program Let's look at a simple simple test program using standard C-style arrays. The following sample is a modified version of the sort_example.cpp program. The program was modified to work with C-Set++. Also note that the STL header file algobase.h had the definitions for min and max commented out to prevent conflict with the min and max macros defined in C-Set++. #include "defalloc.h" #include "algo.h" #include "tempbuf.cpp" // Can also just compile and link with this file #include "heap.h" #include "function.h" #include "random.cpp" // Can also just compile and link with this file //Rand is used to fill the array with sequential numbers class Rand{ int I; public: Rand(unsigned long X):I(X){} int operator()(){return I++;} }; void main(int argc, char *argv[]){ int *last1; int *middle; int test_sequence1[15]; middle = test_sequence1 + 10; last1 = test_sequence1 + 15; ostream_iterator out_stream(cout, " "); less less_than; generate((int*)test_sequence1, last1, Rand(0)); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle((int*)test_sequence1, last1); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) sort((int*)test_sequence1, last1); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle((int*)test_sequence1, last1); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) sort((int*)test_sequence1, last1, less_than); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle((int*)test_sequence1, last1); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) stable_sort((int*)test_sequence1, last1); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle((int*)test_sequence1, last1); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) stable_sort((int*)test_sequence1, last1, less_than); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle((int*)test_sequence1, last1); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) partial_sort((int*)test_sequence1, middle, last1); copy((int*)test_sequence1, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 1? 1? 1? 1? 1? } In the above example, last1 and middle are iterators. The array test_sequence1[15] is the container. Basically, the sample involves filling the container, shuffling the elements and then sorting them using the various sort algorithms. Similarly, one can use a vector container. A vector container is the closest thing to a C array. The follow example uses a vector instead of an array. #include "defalloc.h" #include "algo.h" #include "tempbuf.cpp" // Can also just compile and link with this file #include "heap.h" #include "function.h" #include "vector.h" #include "random.cpp" // Can also just compile and link with this file //Rand is used to fill the array with sequential numbers class Rand{ int I; public: Rand(unsigned long X):I(X){} int operator()(){return I++;} }; void main(int argc, char *argv[]){ vector::iterator Start; vector::iterator last1; vector::iterator middle; vector test_sequence1(15); ostream_iterator out_stream(cout, " "); less less_than; Start=test_sequence1.begin(); last1 = Start + 15; generate(Start, last1, Rand(0)); Start=test_sequence1.begin(); middle = Start + 10; last1 = Start + 15; copy(Start, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle(Start, last1); copy(Start, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) sort(Start, last1); copy(Start, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle(Start, last1); copy(Start, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) sort(Start, last1, less_than); copy(Start, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle(Start, last1); copy(Start, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) stable_sort(Start, last1); copy(Start, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle(Start, last1); copy(Start, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) stable_sort(Start, last1, less_than); copy(Start, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 random_shuffle(Start, last1); copy(Start, last1, out_stream), cout << endl; // Output: (the sequence of 0 through 14 with elements in random order) partial_sort(Start, middle, last1); copy(Start, last1, out_stream), cout << endl; // Output: 0 1 2 3 4 5 6 7 8 9 1? 1? 1? 1? 1? } As before, last1 and middle are iterators. The container is test_sequence1. However, a new iterator called Start is introduced. In the first example, the container could double as the starting iterator because the container was simply a pointer. In the second sample, the container is a vector class. The vector class can't act as an iterator so we use Start. In our last example, we will use the set manipulation functions to create a vector based upon 2 other vectors. typedef vector IVec; void main(int argc, char *argv[]){ vector Set1,Set2,Output; ostream_iterator out_stream(cout, " "); less less_than; //let's insert some values at the END // of the vector Set1.insert(Set1.end(),10); Set1.insert(Set1.end(),11); Set1.insert(Set1.end(),2); Set1.insert(Set1.end(),1); Set1.insert(Set1.end(),12); Set1.insert(Set1.end(),5); Set2.insert(Set2.end(),10); Set2.insert(Set2.end(),5); Set2.insert(Set2.end(),2); Set2.insert(Set2.end(),20); Set2.insert(Set2.end(),24); //let's display our two vectors cout<<"Set 1"<(Output,Output.begin())); cout<<"Union"<(Output,Output.begin())); cout<<"Intersection"<(Output,Output.begin())); cout<<"Difference Set1 <-> Set2"<(Output,Output.begin())); cout<<"Difference Set2 <-> Set1"<