http://www.edm2.com/api.php?action=feedcontributions&user=Doodle&feedformat=atomEDM2 - User contributions [en]2024-03-29T08:46:24ZUser contributionsMediaWiki 1.22.2http://www.edm2.com/index.php/Main_PageMain Page2005-06-10T22:50:54Z<p>Doodle: Added Part 3 of article 'Creating modules for Doodle's Screen Saver'</p>
<hr />
<div>'''Welcome to the relaunched EDM/2 site!'''<br />
<br />
It's about five years ago that the last issue of EDM/2 was released to the public, since then no new articles about OS/2 and eCS programming appeared on EDM/2. So it was about time to finally change that! ([[EDM2:About|A short history of EDM/2]])<br />
<br />
For sure we had to face the facts too, the OS/2 and eCS developer community shrank quite a lot so monthly issues are simply impossible nowadays. It would just be a lot of work for the editor and we seriously doubt that we would find enough articles for an ''issue''. If we wait each time to fill a new issue we would not release written articles to the public for a too long time so we decided to go another way that popped up: [http://en.wikipedia.org/wiki/Wiki Wiki]<br><br />
<br />
This basically means that everyone of you out there can contribute to the new EDM/2 without a lot of interaction of other people and even better, the community can extend the articles and fix problems!<br />
<br />
So far, enjoy the new stuff and don't forget to [[contribute]]!<br />
<br />
The new EDM/2 staff<br />
<br />
(aka the crew from [http://www.netlabs.org/ netlabs.org] :)<br />
<br />
<table><br />
<tr style="vertical-align:top;"><br />
<td style="width:50%; width:50%; border: 1px solid #ffc9c9; padding: .5em 1em 1em; color: #000000; background-color: #fff3f3"><br />
<h3>Latest articles</h3><br />
* [[Ft2lib| Using Innotek FT2LIB for your own applications]] by [[user:Simpson_2|Bart van Leeuwen]]<br />
* [[SDL|Porting SDL applications]] from [http://www.netlabs.org/ netlabs.org]<br />
* [[How to program for the WPS]] from [http://www.netlabs.org/ netlabs.org]<br />
* [[Free Pascal Compiler Review]] by [[Prokushev]]<br />
* [[Creating modules for Doodle's Screen Saver - Part 1]] by [[Doodle]]<br />
* [[Creating modules for Doodle's Screen Saver - Part 2]] by [[Doodle]]<br />
* [[Creating modules for Doodle's Screen Saver - Part 3]] by [[Doodle]]<br />
* [[How to setup old Borland C for use with eComStation Toolkit]] by [[Anakor]]<br />
* [[Porting from Unix platforms]] by [[Ydario]]<br />
* [[Introduction to SOM]] by [[Prokushev]]<br />
<br />
<h3>Republished articles</h3><br />
* [[Introduction to PM Programming - Dec 1993]] by [[Larry Salomon Jr.]]<br />
* [[Introduction to PM Programming - Jan 1994]] by [[Larry Salomon Jr.]]<br />
* [[Introduction to PM Programming - Feb 1994]] by [[Larry Salomon Jr.]]<br />
* [[Progress1|A Progress-indicating Status Line in C++ - Part 1/3]] by [[Stefan Ruck]] (June 1996)<br />
* [[Progress2|A Progress-indicating Status Line in C++ - Part 2/3]] by [[Stefan Ruck]] (July 1996)<br />
* [[Progress3|A Progress-indicating Status Line in C++ - Part 3/3]] by [[Stefan Ruck]] (August 1996)<br />
* [[Manage Your Configuration Files and Data]] by [[Stefan Ruck]] (September 1997)<br />
* [[Multilingual Resources]] by [[Stefan Ruck]] (November 1997)<br />
* [[Let's Talk About... Singleton]] by [[Stefan Ruck]] (December 1997)<br />
* [[Mnemonics in Dialog Boxes]] by [[Stefan Ruck]] (September 1998)<br />
* [[A Keystroke Recorder]] by [[Stefan Ruck]] (January 1999)<br />
* [[Singletons - The Next Generation]] by [[Stefan Ruck]] (September 1999)<br />
* [[OS/2 Frequently Asked Questions]] by [[Dean Roddey]] (November 1995)<br />
* [[Stupid Enumeration Tricks]] by [[Dean Roddey]] (June 1996) <br />
* [[Controlling Yourself. A Framework for Configurable Options]] by [[John Holt]] (December 1994) <br />
* [[Fitting a Notebook into a Dialog]] by [[Roman Stangl]] (July 1997)<br />
* [[Notebook Key Processing]] by [[Roman Stangl]] (August 1997)<br />
* [[Rebooting OS/2]] by [[Roman Stangl]] (September 1997)<br />
* [[Disabling a Window (Rectangle)]] by [[Roman Stangl]] (October 1997)<br />
* [[Managing DOS Settings]] by [[Roman Stangl]] (July 1998)<br />
* [[Calling PM from AVIO Applications]] by [[Roman Stangl]] (August 1998)<br />
* [[Resources and Decompiling Them]] by [[Martin Lafaix]] (June 1994)<br />
* [[KEYBOARD.DCP File Format]] by [[Martin Lafaix]] (March 1995)<br />
* [[Implementing Flyout Menus in OS/2]] by [[Mike La Martina]] (May 1999)<br />
* [[OS/2's Symmetrical Multiprocessing Demystified]] by [[Ted Waldron III]] (September 1997)<br />
</td><br />
<td style="border: 1px solid #c6c9ff; padding: .5em 1em 1em; color: #000000; background-color: #f0f0ff"><br />
<h3>Categories</h3><br />
* [[SOM]] - SOM, DSOM, WPS, OpenDoc <br />
* [[Driver]] - Device Drivers, IFS<br />
* [[Scripting]] - REXX, DLLs for use with REXX and other scripting stuff <br />
* [[Multimedia]] - Media players, IO Procedures, Graphics stuff<br />
* [[Languages]] - OS/2 and eCS related information for programming languages<br />
* [[Documentation]] - a collection of other ressources like online books, articles etc...<br />
* [[Porting]] - porting applications from Unix/X11/Windows...<br />
* [[Games]] - games development and porting...<br />
* [[Developer Tools Section|Tools]] - development tools and utilities...<br />
* [[Localization]] - How to optimize for NLS and use location specific information<br />
<h3>General Stuff</h3><br />
* our [[Mailinglist]] for EDM/2<br />
* [[contributors|Authors and contributors]]<br />
* [[Developer Tools Section]]<br />
* the [http://www.edm2.com/indexold.html original EDM/2 pages], including the [http://www.edm2.com/common/metaindex.html Meta Index]<br />
* information about how you can [[contribute]]<br />
* some words about the [[license]] of the documents on this page<br />
* translating articles to other [[Contribute#Translation_of_articles|languages]]<br />
<br />
</td><br />
</tr><br />
</table></div>Doodlehttp://www.edm2.com/index.php/Creating_modules_for_Doodle%27s_Screen_Saver_-_Part_3Creating modules for Doodle's Screen Saver - Part 32005-06-08T10:43:05Z<p>Doodle: First version</p>
<hr />
<div>== Introduction ==<br />
<br />
Starting with the v1.5 version of Doodle's Screen Saver, the package contains a new module called 'Pretty Clock'. This screen saver module shows a skinnable clock on the screen.<br />
<br />
The purpose of this article is to describe the format used for the skins, so people might be able to create their own clock skins.<br />
<br />
== The basics ==<br />
<br />
The Pretty Clock module assembles its clock image from five different layers. All these layers are transparent PNG files. These layers are the following:<br />
<br />
* Background<br />
* Hour marker<br />
* Minute marker<br />
* Second marker<br />
* Foreground<br />
<br />
As you can imagine, a nice watch can be constructed using this method by putting the image of the watch to the background, putting the markers on top of it, then putting another transparent image on top of these to imitate the glass of the watch.<br />
<br />
== To Rotate Or Not To Rotate ==<br />
<br />
Now, to make things a bit more flexible (and a bit more confusing), the drawing of the markers have been made special.<br />
<br />
Imagine, that you have an image of the hour marker, pointing to 12:00. If the time is 06:00, then it has to be rotated by 180 degrees. However, if you want your marker to be very nice, you draw shadows to it, but a rotated version of this image will look ugly, because it will rotate the shadows, too.<br />
<br />
So, it was decided that the skin designers can decide which statuses they want to draw. It's possible to draw 24 different images for the hour marker, one for each possible position of the marker. If the code finds an exact image for the given hour, it will use that one, or it will find the closest match, rotate that image, and use the rotated image.<br />
<br />
The same applies to the minute and second markers. It's possible to have only one image of them, then that image will be rotated. However, it's also possible to draw all the possible positions of the markers, then those images will be used.<br />
<br />
It's also possible to draw only some of the positions of the markers, like 0, 15, 30 and 45 minutes, or any combination you like, the missing ones will be created automatically by rotating the closest available image.<br />
<br />
Simple, no?<br />
<br />
Yes? Well, then here comes one more twirl.<br><br />
As you might be aware, there are clocks where the hour and minute markers jump from one position to another, and there are ones where they slowly advance the next position as the time goes. This later case is called<br />
"analogue mode" in Pretty Clock, and both the hour and the minute markers can be told to work in analogue mode.<br />
<br />
== The skin descriptor file ==<br />
<br />
You may ask now how to describe all these to the screen saver module?<br><br />
Well, all these are described in a simple text file, which has the extension of ".skn". You will already find some skin files installed in "Modules\PrClock" subdirectory of the Doodle's Screen Saver home folder, now that's the place where you should copy your own skins to install them.<br />
<br />
And now let's see the keywords you can use in the skin file:<br />
<br />
=== Skin name ===<br />
<br />
A short description or the name of the skin can be told by using the keyword "Skin_Name".<br><br />
For example:<br />
Skin_Name = Test skin by Doodle<br />
<br />
=== Draw area ===<br />
<br />
All the layers are drawn inside a so-called Draw Area. The draw area is a rectangular drawing space, in which all drawing is done. This area is moved around the screen as the screen saving goes, so your pretty clock will not be burned into the monitor.<br />
<br />
The size of the Draw Area can be told by using the following keywords:<br />
Draw_Area_Width = 256<br />
Draw_Area_Height = 256<br />
<br />
=== The images ===<br />
<br />
All the images of the layers can be described by using two keywords for the given image. The first one tells the PNG file to use, for example:<br />
Background_Image = test\test_back.png<br />
(All the file paths are relative to the place of the skin file.)<br />
<br />
The second one tells the center of the image, so the module will know how to rotate that image if needed. It's not used for the foreground and background images, but it can be used for the markers:<br />
Hour_0_Image = test\test_stick.png<br />
Hour_0_Image_Center = 128 128 64 64<br />
<br />
The four numbers are: <CenterOffsetX> <CenterOffsetY> <CenterX> <CenterY><br />
<br />
The CenterX and CenterY values tell the center of the PNG image. The image will be rotated around this point, if it has to be rotated. Most of the times it's the center of the image, but it can be set so that the image will be rotated around its bottom or anything else.<br />
<br />
The CenterOffsetX and CenterOffsetY values tell a coordinate inside the Draw Area, where the center of the image (given above) will be positioned. This is usually the center of the Draw Area.<br />
<br />
Please note that all coordinates are relative to top-left corner, which is (0;0).<br />
<br />
The following images can be specified:<br />
<br />
"Background_Image" and "Background_Image_Center"<br><br />
"Foreground_Image" and "Foreground_Image_Center"<br><br />
"Hour_0_Image", "Hour_1_Image" ... "Hour_23_Image" and the corresponding "Hour_0_Image_Center", "Hour_1_Image_Center" ... "Hour_23_Image_Center"<br><br />
"Minute_0_Image", "Minute_1_Image" ... "Minute_59_Image" and the corresponding "Minute_0_Image_Center", "Minute_1_Image_Center" ... "Minute_59_Image_Center"<br><br />
"Second_0_Image", "Second_1_Image" ... "Second_59_Image" and the corresponding "Second_0_Image_Center", "Second_1_Image_Center" ... "Second_59_Image_Center"<br><br />
<br />
=== Analogue modes ===<br />
<br />
As it was already described, the hour and minute markers can work in "analogue mode", where they do not jump from one position to another, but slowly advance it. This can be turned on and off by setting the value of<br />
the following two keywords to 1 or 0:<br />
Hour_Analogue_Mode = 1<br />
Minute_Analogue_Mode = 1<br />
<br />
== Conclusion ==<br />
<br />
As you can see, creating new clock skins is not hard. You only need some inspiration, some drawing tools, create your images, assemble your SKN file, and you're done.<br />
<br />
All you have to take care is to make sure that all your PNG files are transparent! If a given PNG file is not transparent, it will not be loaded by Pretty Clock, so your skin will either not be loaded at all, or some parts of it will be missing.<br />
<br />
Good luck with creating your new skins!</div>Doodlehttp://www.edm2.com/index.php/Porting_SDL_applications_to_OS/2Porting SDL applications to OS/22005-04-03T15:46:48Z<p>Doodle: /* Using the OpenWatcom compiler */</p>
<hr />
<div>== Porting SDL applications to OS/2 ==<br />
<br />
=== Requirements ===<br />
<br />
To port SDL applications to OS/2, you'll need the latest SDL binaries<br />
and header files from netlabs.org. Currently the OpenWatcom compiler is<br />
<b>the</b> compiler for the OS/2 port of SDL, but it should also work<br />
with GCC.<br />
<br />
=== Using the OpenWatcom compiler ===<br />
<br />
There are some requirements and tricks with using the OpenWatcom compiler<br />
to port SDL applications. You should note the followings things.<br />
<br />
==== Use the -ei switch for the OpenWatcom C compiler ====<br />
<br />
You have to use the <i>-ei</i> switch for the OpenWatcom C compiler to<br />
have the size of enums equal to the size of ints. This is a requirement of<br />
SDL itself.<br />
<br />
==== Use the -5s switch for the OpenWatcom C compiler ====<br />
<br />
Starting with the build of 2005.03.30. of SDL/2, the calling convention has been<br />
changed to be stack-based (switch <i>-5s</i>) instead of the previous register-based<br />
one (switch <i>-5r</i> or simply <i>-5</i>).<br />
<br />
Although the register-based version was faster (in theory), the calling convention has<br />
been changed to be compatible with GCC, so people using GCC can use SDL.DLL without<br />
problems, even where SDL calls into the GCC code (e.g. audio mixer callback), because <br />
now both of them use the very same convention.<br />
<br />
So, don't forget to use the <i>-5s</i> switch for the OpenWatcom C compiler if you use<br />
that to compile your SDL application!<br />
<br />
==== Wrapping the SDL_Quit() function ====<br />
<br />
The OS/2 version of SDL is compiled into a DLL file. This DLL file has all<br />
the SDL functions exported by name. In order to be able to use this DLL from<br />
most of the available compilers, these exports are using the <i>_Syscall</i> <br />
calling convention. This leads to problems with some SDL apps, where they assume<br />
that the calling convention of the API is the same as the calling convention of<br />
their runtime library. For example, the following line is very usual in SDL<br />
applications:<br />
<br />
...<br />
atexit(SDL_Quit);<br />
...<br />
<br />
This should be worked around by creating a wrapper function for SDL_Quit, and<br />
using that one, this way:<br />
<br />
#ifdef __WATCOMC__<br />
void SDL_Quit_Wrapper()<br />
{<br />
SDL_Quit();<br />
}<br />
#endif<br />
...<br />
#ifdef __WATCOMC__<br />
atexit(SDL_Quit_Wrapper);<br />
#else<br />
atexit(SDL_Quit);<br />
#endif;<br />
<br />
=== Using the GNU C Compiler ===<br />
<br />
It was reported that some (or maybe all) of the GNU C compilers<br />
(GCC) available for OS/2 can not handle the SDL.LIB file included in<br />
the SDL DevPack. The problem is that the SDL.LIB file was created<br />
directly from the SDL.DLL file, using the IMPLIB.EXE tool from the<br />
OS/2 Developer's Toolkit, and the GCC does not recognize it to be a<br />
valid library file.<br />
<br />
There is a workaround, which is reported to work in this case. One has<br />
to create a GCC-compatible library file (.a), using the EMXIMP tool,<br />
and link against that file:<br />
<br />
emximp -o sdl.imp sdl.lib<br />
emximp -o sdl.a sdl.imp<br />
<br />
If you plan on using SDL's multithreading capabilities (e.g. if you want<br />
to include "SDL_thread.h"), be sure that enable multithreading with -Zmt<br />
on your GCC commandline, otherwise you'll get an error because _beginthread()<br />
and _endthread() will not be defined.<br />
<br />
=== General tips and tricks ===<br />
<br />
==== Morph your program to a PM program at runtime ====<br />
<br />
Most SDL applications assume that they can write to STDOUT, and the user will<br />
see it. In OS/2, you have to compile your application to a PM application in <br />
order to be able to have a PM Window, but the STDOUT will go to NULL in this <br />
case. The solution is to compile your code to a VIO application, and morph the<br />
application to a PM one dynamically, at runtime, before calling SDL_Init().<br />
<br />
Once you have your text output back, you might use it for showing debug messages.<br />
If you do so, it's a good idea to make the STDOUT and STDERR unbuffered, so<br />
the debug messages will be shown right at the time when the printf() is called,<br />
so every message will be visible even in case of a crash.<br />
<br />
Here is an example code on how to morph your VIO application to a PM one<br />
dynamically, at runtime, and how to set STDOUT and STDERR unbuffered:<br />
<br />
#define INCL_DOS<br />
#include <os2.h><br />
#include <stdio.h><br />
<br />
...<br />
<br />
void MorphToPM()<br />
{<br />
PPIB pib;<br />
PTIB tib;<br />
<br />
DosGetInfoBlocks(&tib, &pib);<br />
<br />
// Change flag from VIO to PM:<br />
if (pib->pib_ultype==2) pib->pib_ultype = 3;<br />
}<br />
<br />
...<br />
<br />
int main(int argc, char *argv[])<br />
{<br />
MorphToPM(); // Morph the VIO application to a PM one to be able to use Win* functions<br />
<br />
// Make stdout and stderr unbuffered<br />
setbuf( stdout, NULL );<br />
setbuf( stderr, NULL );<br />
<br />
/* Initialize SDL */<br />
if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {<br />
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());<br />
exit(1);<br />
}<br />
<br />
...</div>Doodlehttp://www.edm2.com/index.php/Main_PageMain Page2005-01-10T16:49:17Z<p>Doodle: Added article "Creating modules for Doodle's Screen Saver - Part 2"</p>
<hr />
<div>'''Welcome to the relaunched EDM/2 site!'''<br />
<br />
It's about five years ago that the last issue of EDM/2 was released to the public, since then no new articles about OS/2 and eCS programming appeared on EDM/2. So it was about time to finaly change that! ([[EDM2:About|A short history of EDM/2]])<br />
<br />
For sure we had to face the facts too, the OS/2 and eCS developer community shrank quite a lot so monthly issues are simply impossible nowadays. It would just be a lot of work for the editor and we seriously doubt that we would find enough articles for an ''issue''. If we wait each time to fill a new issue we would not release written articles to the public for a too long time so we decided to go another way that popped up: [http://en.wikipedia.org/wiki/Wiki Wiki]<br><br />
<br />
This basically means that everyone of you out there can contribute to the new EDM/2 without a lot of interaction of other people and even better, the community can extend the articles and fix problems!<br />
<br />
So far, enjoy the new stuff and don't forget to [[contribute]]!<br />
<br />
The new EDM/2 staff<br />
<br />
(aka the crew from [http://www.netlabs.org/ netlabs.org] :)<br />
<br />
<table><br />
<tr style="vertical-align:top;"><br />
<td style="width:50%; width:50%; border: 1px solid #ffc9c9; padding: .5em 1em 1em; color: #000000; background-color: #fff3f3"><br />
<h3>Latest articles</h3><br />
* [[Creating modules for Doodle's Screen Saver - Part 1]] by [[Doodle]]<br />
* [[Creating modules for Doodle's Screen Saver - Part 2]] by [[Doodle]]<br />
* [[How to setup old Borland C for use with eComStation Toolkit]] by [[Anakor]]<br />
* [[Porting from Unix platforms]] by [[Ydario]]<br />
* [[Introduction to SOM]] by [[Prokushev]]<br />
<br />
<h3>Republished articles</h3><br />
* [[Progress1|A Progress-indicating Status Line in C++ - Part 1/3]] by [[Stefan Ruck]] (June 1996)<br />
* [[Progress2|A Progress-indicating Status Line in C++ - Part 2/3]] by [[Stefan Ruck]] (July 1996)<br />
* [[Progress3|A Progress-indicating Status Line in C++ - Part 3/3]] by [[Stefan Ruck]] (August 1996)<br />
* [[Manage Your Configuration Files and Data]] by [[Stefan Ruck]] (September 1997)<br />
* [[Multilingual Resources]] by [[Stefan Ruck]] (November 1997)<br />
* [[Let's Talk About... Singleton]] by [[Stefan Ruck]] (December 1997)<br />
* [[Mnemonics in Dialog Boxes]] by [[Stefan Ruck]] (September 1998)<br />
* [[A Keystroke Recorder]] by [[Stefan Ruck]] (January 1999)<br />
* [[Singletons - The Next Generation]] by [[Stefan Ruck]] (September 1999)<br />
* [[OS/2 Frequently Asked Questions]] by [[Dean Roddey]] (November 1995)<br />
* [[Stupid Enumeration Tricks]] by [[Dean Roddey]] (June 1996) <br />
* [[Controlling Yourself. A Framework for Configurable Options]] by [[John Holt]] (December 1994) <br />
* [[Gearing Up For Games]] by [[Michael T. Duffy]] (June 1995)<br />
* [[Gearing Up For Games - Part 2]] by [[Michael T. Duffy]] (August 1995)<br />
* [[Gearing Up For Games - Part 3]] by [[Michael T. Duffy]] (September 1995)<br />
* [[Customizing the Enhanced Editor]] by [[Jörg Schwieder]] (November 1993) <br />
* [[Adding BLDLEVEL information to executables]] by [[Roman Stangl]] (June 1997)<br />
* [[Fitting a Notebook into a Dialog]] by [[Roman Stangl]] (July 1997)<br />
* [[Notebook Key Processing]] by [[Roman Stangl]] (August 1997)<br />
* [[Rebooting OS/2]] by [[Roman Stangl]] (September 1997)<br />
* [[Disabling a Window (Rectangle)]] by [[Roman Stangl]] (October 1997)<br />
* [[Managing DOS Settings]] by [[Roman Stangl]] (July 1998)<br />
* [[Calling PM from AVIO Applications]] by [[Roman Stangl]] (August 1998)<br />
* [[Using SYSLEVEL Files in Your Applications]] by [[Martin Lafaix]] (May 1994)<br />
* [[Resources and Decompiling Them]] by [[Martin Lafaix]] (June 1994)<br />
* [[KEYBOARD.DCP File Format]] by [[Martin Lafaix]] (March 1995)<br />
* [[Implementing Flyout Menus in OS/2]] by [[Mike La Martina]] (May 1999)<br />
* [[OS/2's Symmetrical Multiprocessing Demystified]] by [[Ted Waldron III]] (September 1997)<br />
* [[The Anon CVS Bazaar - Part I]] by [[Henry Sobotka]] (April 1999)<br />
* [[The Anon CVS Bazaar - Part II]] by [[Henry Sobotka]] (May 1999)<br />
* [[A Warped RCS/CVS HowTo]] by [[Henry Sobotka]] (May 1999)<br />
* [[OS/2 Warp Game Developer's Seminar]] by [[Dave Briccetti]] (September 1995)<br />
* [[Sending Mail with REXX]] by [[Dave Briccetti]] (December 1995) <br />
<br />
<br />
<br />
</td><br />
<td style="border: 1px solid #c6c9ff; padding: .5em 1em 1em; color: #000000; background-color: #f0f0ff"><br />
<h3>Categories</h3><br />
* [[SOM]] - WPS, SOM, DSOM, OpenDoc <br />
* [[Driver]] - Device Drivers, IFS<br />
* [[Scripting]] - REXX, DLLs for use with REXX and other scripting stuff <br />
* [[Multimedia]] - Media players, IOCodecs, Graphics stuff<br />
* [[Languages]] - OS/2 and eCS related information for programming languages<br />
* [[Documentation]] - a collection of other ressources like online books, articles etc...<br />
* [[Porting]] - porting applications from Unix/X11/Windows...<br />
<h3>General Stuff</h3><br />
* our [[Mailinglist]] for EDM/2, <br />
* Authors and [[contributors]]<br />
* the [http://www.edm2.com/indexold.html original EDM/2 pages], including the [http://www.edm2.com/common/metaindex.html Meta Index]<br />
* information about how you can [[contribute]]<br />
* some words about the [[license]] of the documents on this page<br />
* translating articles to other [[Contribute#Translation_of_articles|languages]]<br />
<br />
</td><br />
</tr><br />
</table></div>Doodlehttp://www.edm2.com/index.php/Creating_modules_for_Doodle%27s_Screen_Saver_-_Part_2Creating modules for Doodle's Screen Saver - Part 22005-01-10T16:43:45Z<p>Doodle: </p>
<hr />
<div>== Introduction ==<br />
<br />
The purpose of these articles about ''dssaver'' is to help people to create new screen saver modules for Doodle's Screen Saver. These articles are meant to be something like a tutorial.<br />
<br />
If you want to follow the article, and compile the source code we'll put together here, you should have the source code of ''dssaver'', downloadable from its [http://dssaver.netlabs.org homepage]. You'll not need everything from that package, only the header file for saver modules, called '''SSModule.h'''. However, it's much easier if you take the source code of an already existing saver module, and modify that to fit your needs.<br />
<br />
== Creating a simple module ==<br />
<br />
We're going to create a simple saver module for ''dssaver'' in this article. The saver module will imitate the old Lockup facility, so a golden padlock will be moving around the screen.<br />
<br />
This module can be done very quickly by modifying the eCSBall module. The modifications required are:<br />
* Replace eCS logo with image of golden padlock<br />
* Modify code so it will move in a line instead of jumping all around the screen every 30 seconds<br />
<br />
To make all these modifications understandable, I'll start here with describing how the old module works. Then we'll see what functions has to be changed and how, to get a new module from the old one.<br />
<br />
== The internals of eCSBall module ==<br />
<br />
So, let's go through the eCSBall saver module, to see how the things are implemented in it. The source code can be found at the [http://dssaver.netlabs.org homepage] or the screen saver, in the ZIP file containing the sources. The source code of the eCSBall module is in the folder Modules\eCSBall.<br />
<br />
=== The easy part ===<br />
<br />
First of all, we handle the DLL initialization/termination, so <br />
* we can save the DLL module handle at initialization time, used to load resources from the DLL, and<br />
* we can do cleanup in case something would be left initialized when the DLL is being unloaded.<br />
<br />
This is done by a special function called ''LibMain'' (at least with the Watcom compiler). This is a function with two parameters, first is the module handle, and the second tells if the module is being loaded or unloaded.<br />
<br />
We do some initialization and termination work in there:<br />
<br />
unsigned _System LibMain(unsigned hmod, unsigned termination)<br />
{<br />
if (termination)<br />
{<br />
// Cleanup!<br />
if (bRunning)<br />
SSModule_StopSaving();<br />
} else<br />
{<br />
// Startup!<br />
hmodOurDLLHandle = (HMODULE) hmod;<br />
bRunning = FALSE;<br />
}<br />
return 1;<br />
}<br />
<br />
Now we can go for the screen saver module functions that must be implemented. The easiest one is the SSModule_GetModuleDesc(), which is called by the screen saver core to get information about the saver module. In eCSBall, we have this, which simply fills the given structure, I don't think it requires too much description:<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_GetModuleDesc(SSModuleDesc_p pModuleDesc)<br />
{<br />
if (!pModuleDesc)<br />
return SSMODULE_ERROR_INVALIDPARAMETER;<br />
<br />
// Return info about module!<br />
pModuleDesc->iVersionMajor = 1;<br />
pModuleDesc->iVersionMinor = 20;<br />
strcpy(pModuleDesc->achModuleName, "eCS Ball");<br />
strcpy(pModuleDesc->achModuleDesc,<br />
"eComStation logo moving around every 30 seconds.\n"<br />
"Written by Doodle"<br />
);<br />
<br />
pModuleDesc->bConfigurable = FALSE;<br />
pModuleDesc->bSupportsPasswordProtection = TRUE;<br />
<br />
return SSMODULE_NOERROR;<br />
}<br />
<br />
The next function in the list of mandatory functions from the previous article, is SSModule_Configure(). This is the easiest part, because this module can not be configured (as it's also described in the previous function by setting pModuleDesc->bConfigurable to FALSE), so it only has to return this information:<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_Configure(HWND hwndOwner, char *pchHomeDirectory)<br />
{<br />
// This stuff here should create a window, to let the user<br />
// configure the module. The configuration should be read and saved from/to<br />
// a private config file in pchHomeDirectory, or in INI files.<br />
<br />
return SSMODULE_ERROR_NOTSUPPORTED;<br />
}<br />
<br />
=== The hard part ===<br />
<br />
And here comes the harder part, doing the real work.<br />
<br />
The SSModule_StartSaving() function is the one which has to start the screen saver module, and then return if it could start it or not. It is described in the previous article, that this function should start a new thread which will do the real saving, wait for it, to see if that thread can set up its job or not, and return success or failure.<br />
<br />
The start parameters have to be passed to the new thread (''saver thread''), so it will know if it has to subclass the parent window (if bPreviewMode is TRUE), or if it has to create a new window (if bPreviewMode is FALSE), and it will know that parent window, too.<br />
<br />
The preview flag is saved into a global variable, because it will be needed from other places too (like the window procedure), so only one parameter remains, the hwndParent, which will be passed to the thread as the thread initializer.<br />
<br />
The syncronization between the saver thread and this function is done via a global variable, called iSaverThreadState, which is set to a constant of STATE_UNKNOWN initially, and which is set by the saver thread to STATE_RUNNING if it could set up everything, or to STATE_STOPPED_ERROR, if it has been stopped, and could not set up the required things.<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_StartSaving(HWND hwndParent, char *pchHomeDirectory, int bPreviewMode)<br />
{<br />
// This is called when we should start the saving.<br />
// Return error if already running, start the saver thread otherwise!<br />
<br />
if (bRunning)<br />
return SSMODULE_ERROR_ALREADYRUNNING;<br />
<br />
iSaverThreadState = STATE_UNKNOWN;<br />
bOnlyPreviewMode = bPreviewMode;<br />
tidSaverThread = _beginthread(fnSaverThread,<br />
0,<br />
1024*1024,<br />
(void *) hwndParent);<br />
<br />
if (tidSaverThread<=0)<br />
{<br />
#ifdef DEBUG_LOGGING<br />
AddLog("[SSModule_StartSaving] : Error creating screensaver thread!\n");<br />
#endif<br />
// Error creating screensaver thread!<br />
return SSMODULE_ERROR_INTERNALERROR;<br />
}<br />
<br />
// Wait for saver thread to start up!<br />
while (iSaverThreadState==STATE_UNKNOWN) DosSleep(32);<br />
if (iSaverThreadState!=STATE_RUNNING)<br />
{<br />
#ifdef DEBUG_LOGGING<br />
AddLog("[SSModule_StartSaving] : Something went wrong in screensaver thread!\n");<br />
#endif<br />
<br />
// Something wrong in saver thread!<br />
DosWaitThread(&tidSaverThread, DCWW_WAIT);<br />
return SSMODULE_ERROR_INTERNALERROR;<br />
}<br />
<br />
// Fine, screen saver started and running!<br />
bRunning = TRUE;<br />
return SSMODULE_NOERROR;<br />
}<br />
<br />
The only question is what does this saver thread look like.<br />
<br />
As it was described before, the saver thread is the one which creates the new window or subclasses the old one, and has the message loop for that window, so, it should run in a bit higher priority than others, to be able to quickly server messages arriving to the window from the Presentation Manager.<br />
<br />
void fnSaverThread(void *p)<br />
{<br />
HWND hwndParent = (HWND) p;<br />
HWND hwndOldFocus;<br />
HWND hwndOldSysModal;<br />
HAB hab;<br />
QMSG msg;<br />
ULONG ulStyle;<br />
<br />
// Set our thread to slightly more than regular to be able<br />
// to update the screen fine.<br />
DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, +5, 0);<br />
<br />
hab = WinInitialize(0);<br />
hmqSaverThreadMsgQueue = WinCreateMsgQueue(hab, 0);<br />
<br />
After this, it checks if it has to run in preview mode or as a real saver (remember the global variable ''bOnlyPreviewMode'', set by the SSModule_StartSaving()?), and does different things according to the result. First the easier part, when it has to run as a preview window:<br />
<br />
if (bOnlyPreviewMode)<br />
{<br />
PFNWP pfnOldWindowProc;<br />
// We should run in preview mode, so the hwndParent we have is not the<br />
// desktop, but a special window we have to subclass.<br />
pfnOldWindowProc = WinSubclassWindow(hwndParent,<br />
(PFNWP) fnSaverWindowProc);<br />
<br />
// Initialize window proc (simulate WM_CREATE)<br />
WinSendMsg(hwndParent, WM_SUBCLASS_INIT, (MPARAM) NULL, (MPARAM) NULL);<br />
// Also make sure that the window will be redrawn with this new<br />
// window proc.<br />
WinInvalidateRect(hwndParent, NULL, FALSE);<br />
<br />
iSaverThreadState = STATE_RUNNING;<br />
<br />
#ifdef DEBUG_LOGGING<br />
AddLog("[fnSaverThread] : Entering message loop (Preview)\n");<br />
#endif<br />
<br />
// Process messages until WM_QUIT!<br />
while (WinGetMsg(hab, &msg, 0, 0, 0))<br />
WinDispatchMsg(hab, &msg);<br />
<br />
As you can see, it simply subclasses the given parent window with the ''fnSaverWindowProc'', which will be our window procedure. Then tells the window proc that it's alive, so sends a special, private message (WM_SUBCLASS_INIT) to the window, which is now subclassed, so it will be got by the ''fnSaverWindowProc'' function. This makes every initialization to the subclassed window that would be made if the window would have been created originally (it will load the bitmap from the DLL, and initialize the initial position and speed of the padlock, but more about it later...).<br />
<br />
The ''WinInvalidateRect()'' function will invalidate the subclassed window, effectively forcing it to be redrawn, but as it's not subclassed, it's our ''fnSaverWindowProc'' which will get the WM_PAINT message, not the old one.<br />
<br />
If all this is done, the saver thread notifies the SSModule_StartSaving() function that everything is set up, so sets the global variable ''iSaverThreadState'' to STATE_RUNNING, which will result in the SSModule_StartSaving() function returning with SSMODULE_NOERROR.<br />
<br />
Then the message loop starts, and our subclassed window is alive.<br />
<br />
When the screen saver core wants us to stop saving, it calls SSModule_StopSaving(), which will send a WM_QUIT to our window. That will break out of the message loop, so our thread procedure can continue with cleaning up the subclassing:<br />
<br />
// Uinitialize window proc (simulate WM_DESTROY)<br />
WinSendMsg(hwndParent, WM_SUBCLASS_UNINIT, (MPARAM) NULL, (MPARAM) NULL);<br />
// Undo subclassing<br />
WinSubclassWindow(hwndParent,<br />
pfnOldWindowProc);<br />
// Also make sure that the window will be redrawn with the old<br />
// window proc.<br />
WinInvalidateRect(hwndParent, NULL, FALSE);<br />
<br />
iSaverThreadState = STATE_STOPPED_OK;<br />
} else<br />
<br />
Sending another private message to our window procedure, so it can clean up the resources allocated in WM_SUBCLASS_INIT (effectively, releasing the padlock bitmap from memory), then undo the subclassing by restoring the old window procedure of the window.<br />
<br />
The invalidation of the window is done so that the old window procedure can have a chance to restore the contents of the window to the old one. Then the global variable ''iSaverThreadState'' is set to notify the SSModule_StopSaving() function that the saving has been stopped.<br />
<br />
It wasn't that complicated, was it? :)<br />
Well, here comes the more complicated part, anyway.<br />
<br />
If the saver thread has to start real saving, then it has to create a window, which will not release the mouse and the keyboard, and which is on top of everything else. For this, we have to create a system modal window (so the user will not be able to switch away, and the mouse and keyboard focus will stay at our window), and we have to make the window always-on-top (it's a special undocumented window flag).<br />
<br />
{<br />
// We should run in normal mode, so create a new window, topmost, and everything else...<br />
WinRegisterClass(hab, (PSZ) SAVERWINDOW_CLASS,<br />
(PFNWP) fnSaverWindowProc,<br />
CS_SIZEREDRAW | CS_CLIPCHILDREN | CS_CLIPSIBLINGS, 0);<br />
<br />
hwndOldFocus = WinQueryFocus(HWND_DESKTOP);<br />
<br />
// Create the saver output window so that it will be the child of<br />
// the given parent window.<br />
// Make window 'Always on top' because we'll be in real screensaver mode!<br />
ulStyle = WS_VISIBLE | WS_TOPMOST;<br />
hwndSaverWindow = WinCreateWindow(HWND_DESKTOP, SAVERWINDOW_CLASS, "Screen saver",<br />
ulStyle,<br />
0, 0,<br />
(int) WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN),<br />
(int) WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN),<br />
HWND_DESKTOP,<br />
HWND_TOP,<br />
0x9fff, // Some ID....<br />
NULL, NULL);<br />
if (!hwndSaverWindow)<br />
{<br />
#ifdef DEBUG_LOGGING<br />
AddLog("[fnSaverThread] : Could not create window!\n");<br />
#endif<br />
// Yikes, could not create window!<br />
iSaverThreadState = STATE_STOPPED_ERROR;<br />
} else<br />
{<br />
// Cool, window created!<br />
<br />
// Make sure nobody will be able to switch away of it!<br />
// We do this by making the window system-modal, and giving it the focus!<br />
hwndOldSysModal = WinQuerySysModalWindow(HWND_DESKTOP);<br />
WinSetSysModalWindow(HWND_DESKTOP, hwndSaverWindow);<br />
WinSetFocus(HWND_DESKTOP, hwndSaverWindow);<br />
<br />
iSaverThreadState = STATE_RUNNING;<br />
<br />
#ifdef DEBUG_LOGGING<br />
AddLog("[fnSaverThread] : Entering message loop (Real)\n");<br />
#endif<br />
// Process messages until WM_QUIT!<br />
while (WinGetMsg(hab, &msg, 0, 0, 0))<br />
WinDispatchMsg(hab, &msg);<br />
<br />
Note, that the window is created with the very same window procedure (''fnSaverWindow'') that was used for the preview mode. This simplifies things very well, one has to write only one window proc, which will work for both preview mode and real saving.<br />
<br />
When the saving has to be closed, a WM_QUIT will be posted to the window, which will break the loop. The cleanup is very easy after this, simply everything has to be undone. :)<br />
<br />
This includes undoing the system modalling by setting the old system modal window back to system modal, destroying our window to free its resources, and setting back the focus to the old window which had the focus before. This last thing is not so important, but makes things much more comfortable, because if the screen saving has been started and stopped, then the user doesn't have to click to the entry field or whatever place he were before the screen saving, he can continue his work from where he left.<br />
<br />
// Undo system modalling<br />
WinSetSysModalWindow(HWND_DESKTOP, hwndOldSysModal);<br />
// Window closed, so destroy every resource we created!<br />
WinDestroyWindow(hwndSaverWindow);<br />
#ifdef DEBUG_LOGGING<br />
AddLog("[fnSaverThread] : STATE_STOPPED_OK\n");<br />
#endif<br />
// Restore focus to old window<br />
WinSetFocus(HWND_DESKTOP, hwndOldFocus);<br />
iSaverThreadState = STATE_STOPPED_OK;<br />
}<br />
}<br />
<br />
Ok, before stopping the saver thread, we only have to destroy the remaining resources we still have:<br />
<br />
WinDestroyMsgQueue(hmqSaverThreadMsgQueue);<br />
WinTerminate(hab);<br />
<br />
_endthread();<br />
}<br />
<br />
That's for the saver thread. From this point, all the tricks are over, the only limit is you imagination (and your dexterity, of course), what you can do in a window.<br />
<br />
For completeness, let's see the window proc of the eCSBall module, which moves a logo around the screen randomly every 30 seconds!<br />
<br />
The function starts with the initialization part, which is common both for preview mode and real saving mode. The only difference is that the mouse pointer should be hidden in real saving mode, otherwise everything is the same: loading the bitmap from the DLL, and starting a timer to get WM_TIMER messages every 30 seconds, from where we can set new position of the image. Note that to decide if we're in preview mode or not, the global variable ''bOnlyPreviewMode'' is used, which was set by the SSModule_StartSaving() function before.<br />
<br />
MRESULT EXPENTRY fnSaverWindowProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)<br />
{<br />
SWP swpDlg, swpParent;<br />
HWND hwndDlg;<br />
int rc;<br />
<br />
switch( msg )<br />
{<br />
case WM_SUBCLASS_INIT:<br />
case WM_CREATE:<br />
// Any initialization of the window and variables should come here.<br />
<br />
// Randomize<br />
srand(time(NULL));<br />
<br />
// Initial image position<br />
bImagePositionValid = FALSE;<br />
<br />
// Get bitmap<br />
internal_LoadBitmapResource(hwnd);<br />
<br />
ulAnimationTimerID = WinStartTimer(WinQueryAnchorBlock(hwnd), hwnd,<br />
ANIMATION_TIMER_ID, // Timer ID<br />
30000); // Let the image have new position every 30 secs<br />
<br />
// Hide mouse pointer, if we're in real screen-saving mode!<br />
if (!bOnlyPreviewMode)<br />
WinShowPointer(HWND_DESKTOP, FALSE);<br />
break;<br />
<br />
The uninitialization of the window is not complicated, either. It releases the bitmap it has loaded at initialization time, stops the timer, and shows the mouse pointer if it was hidden.<br />
<br />
case WM_SUBCLASS_UNINIT:<br />
case WM_DESTROY:<br />
// All kinds of cleanup (the opposite of everything done in WM_CREATE)<br />
// should come here.<br />
<br />
// Stop timer<br />
WinStopTimer(WinQueryAnchorBlock(hwnd), hwnd,<br />
ulAnimationTimerID);<br />
// Drop bitmap<br />
internal_DestroyBitmapResource();<br />
<br />
// Restore mouse pointer, if we're in real screen-saving mode!<br />
if (!bOnlyPreviewMode)<br />
WinShowPointer(HWND_DESKTOP, TRUE);<br />
<br />
break;<br />
<br />
There is a window message WM_ADJUSTWINDOWPOS, which is sent to a window if its position, size, or Z-order changes. This is the one what we handle in order to stay on top at all costs, if we're screensaving. Note that it's only done if the window is not in preview mode.<br />
<br />
What it does is that it checks the SWP structure it gets in MP1, and filters it so that this window will have the topmost Z-order every time. It also updates wht window style (using WinSetWindowBits()) every time, to make sure that this window remains the topmost window.<br />
<br />
case WM_ADJUSTWINDOWPOS:<br />
if (!bOnlyPreviewMode)<br />
{<br />
SWP *pSWP;<br />
<br />
// The following is required so that this window will be on<br />
// top of the xCenter window, evenif that is set to be always on top!<br />
<br />
// Real screensaving, here we should stay on top!<br />
// Set WS_TOPMOST flag again!<br />
WinSetWindowBits(hwnd, QWL_STYLE, WS_TOPMOST, WS_TOPMOST);<br />
<br />
pSWP = (SWP *) mp1;<br />
pSWP->hwndInsertBehind = HWND_TOP;<br />
pSWP->fl |= SWP_ZORDER;<br />
}<br />
break;<br />
<br />
The next message to handle is WM_PAINT, which is sent when we should draw into our window. This is the place where we should draw the black background, and the eCSBall logo onto it. The only thing which complicates this process is that, if we're in preview mode, we have to scale down the logo, so the preview window will really look like a scaled down version of the full screen window.<br />
<br />
case WM_PAINT:<br />
{<br />
HPS hpsBeginPaint;<br />
RECTL rclRect, rclImage;<br />
SWP swpWindow;<br />
<br />
if (!bImagePositionValid)<br />
internal_MakeRandomImagePosition(hwnd);<br />
<br />
hpsBeginPaint = WinBeginPaint(hwnd, NULLHANDLE, &rclRect);<br />
<br />
WinQueryWindowRect(hwnd, &rclRect);<br />
// Fill with black<br />
WinFillRect(hpsBeginPaint, &rclRect, CLR_BLACK);<br />
<br />
if (hbmImage)<br />
{<br />
if (!bImagePositionValid)<br />
internal_MakeRandomImagePosition(hwnd);<br />
<br />
if (bOnlyPreviewMode)<br />
{<br />
WinQueryWindowPos(hwnd, &swpWindow);<br />
internal_CalculateDestImageSize(hwnd);<br />
rclImage.xLeft = ((long long) ptlImageDestPos.x) * swpWindow.cx / (int) WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);<br />
rclImage.yBottom = ((long long) ptlImageDestPos.y) * swpWindow.cy / (int) WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);<br />
rclImage.xRight = rclImage.xLeft + sizImageDestSize.cx;<br />
rclImage.yTop = rclImage.yBottom + sizImageDestSize.cy;<br />
<br />
WinDrawBitmap(hpsBeginPaint, hbmImage,<br />
NULL,<br />
(PPOINTL) (&rclImage),<br />
CLR_NEUTRAL, CLR_BACKGROUND,<br />
DBM_STRETCH);<br />
} else<br />
{<br />
WinDrawBitmap(hpsBeginPaint, hbmImage,<br />
NULL,<br />
&ptlImageDestPos,<br />
CLR_NEUTRAL, CLR_BACKGROUND,<br />
DBM_NORMAL);<br />
}<br />
}<br />
<br />
WinEndPaint(hpsBeginPaint);<br />
return (MRESULT) FALSE;<br />
}<br />
<br />
We've started a timer in the initialization part, which results in WM_TIMER messages every 30 seconds. When this message arrives with our timer ID, we set a flag that a new image position will have to be calculated, and invalidate the window, so it will get a WM_PAINT to be redrawn with the new position. This is all we have to handle in our window proc right now.<br />
<br />
case WM_TIMER:<br />
if (((SHORT)mp1)==ANIMATION_TIMER_ID)<br />
{<br />
// Timer, so make new image position<br />
bImagePositionValid = FALSE;<br />
WinInvalidateRect(hwnd, NULL, FALSE);<br />
}<br />
break;<br />
default:<br />
break;<br />
}<br />
return WinDefWindowProc( hwnd, msg, mp1, mp2 );<br />
}<br />
<br />
There are some internal functions not described here, like the ''internal_LoadBitmapResource()'', but they can be checked in the original source code, if you're interested.<br />
<br />
=== The not-so-hard part ===<br />
<br />
Now, we're over the hard part. Of course, there are some other functions remaining, what we have to implement. One of them gives the module the ability to stop. :)<br />
<br />
The SSModule_StopSaving() is called when the module has to stop. It's done by posting a WM_QUIT message to the saver window so the message processing loop of the saver thread will break, and the saver thread will do the cleanup.<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_StopSaving(void)<br />
{<br />
// This is called when we have to stop saving.<br />
if (!bRunning)<br />
return SSMODULE_ERROR_NOTRUNNING;<br />
<br />
// Notify saver thread to stop!<br />
if (bOnlyPreviewMode)<br />
{<br />
// In preview mode, which means that there is no window created, but the<br />
// window was subclassed. So we cannot close the window, but we have to<br />
// post the WM_QUIT message into the thread's message queue to notify it<br />
// that this is the end of its job.<br />
WinPostQueueMsg(hmqSaverThreadMsgQueue, WM_QUIT, (MPARAM) NULL, (MPARAM) NULL);<br />
}<br />
else<br />
{<br />
// Close saver window<br />
WinPostMsg(hwndSaverWindow, WM_QUIT, (MPARAM) NULL, (MPARAM) NULL);<br />
}<br />
<br />
// Wait for the thread to stop<br />
DosWaitThread(&tidSaverThread, DCWW_WAIT);<br />
// Ok, screensaver stopped.<br />
bRunning = FALSE;<br />
return SSMODULE_NOERROR;<br />
}<br />
<br />
This is done, too, fine. What remains is the function to ask password from the user (remember that we reported that password protection is supported by this module!), and the function to tell the user if a wrong password has been entered.<br />
<br />
For these, we send special messages to the window, so the window procedure will handle these cases.<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_AskUserForPassword(int iPwdBuffSize, char *pchPwdBuff)<br />
{<br />
// This is called when we should ask the password from the user.<br />
if (!bRunning)<br />
return SSMODULE_ERROR_NOTRUNNING;<br />
<br />
if ((iPwdBuffSize<=0) || (!pchPwdBuff))<br />
return SSMODULE_ERROR_INVALIDPARAMETER;<br />
<br />
return (int) WinSendMsg(hwndSaverWindow, WM_ASKPASSWORD,<br />
(MPARAM) pchPwdBuff,<br />
(MPARAM) iPwdBuffSize);<br />
}<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_ShowWrongPasswordNotification(void)<br />
{<br />
// This is called if the user has entered a wrong password, so we should<br />
// notify the user about this, in a way that fits the best to our screensaver.<br />
<br />
if (!bRunning)<br />
return SSMODULE_ERROR_NOTRUNNING;<br />
<br />
return (int) WinSendMsg(hwndSaverWindow, WM_SHOWWRONGPASSWORD,<br />
(MPARAM) NULL,<br />
(MPARAM) NULL);<br />
}<br />
<br />
Unfortunately, for this to work, we have to insert two more cases into the switch statement of the ''fnSaverWindowProc''. <br />
<br />
The first one handles the private message WM_ASKPASSWORD. It shows a dialog window, centered to the screen, waiting for the user to enter the password, and stores the entered password in the buffer passed in the MP1 parameter of the message.<br />
<br />
The second one simply shows another dialog window, again, centered to the screen, but this dialog window has no entry field for password, only a button to confirm that the user has read its contents about invalid password.<br />
<br />
One thing to note here is that it's not the default dialog procedure which is used for them, but a private one (''fnAutoHiderDialogProc''), which closes the dialog window if the user does not enter any characters for 10 seconds. This prevents these dialog window to stay on screen and burn into the CRT.<br />
<br />
case WM_ASKPASSWORD:<br />
{<br />
// Get parameters<br />
char *pchPwdBuff = (char *) mp1;<br />
int iPwdBuffSize = (int) mp2;<br />
<br />
// Show mouse pointer, if we're screensaving.<br />
if (!bOnlyPreviewMode)<br />
WinShowPointer(HWND_DESKTOP, TRUE);<br />
<br />
hwndDlg = WinLoadDlg(hwnd, hwnd,<br />
fnAutoHiderDlgProc,<br />
hmodOurDLLHandle,<br />
DLG_PASSWORDPROTECTION,<br />
NULL);<br />
if (!hwndDlg)<br />
{<br />
// Could not load dialog window resources!<br />
if (!bOnlyPreviewMode)<br />
WinShowPointer(HWND_DESKTOP, FALSE);<br />
return (MRESULT) SSMODULE_ERROR_INTERNALERROR;<br />
}<br />
<br />
// Ok, dialog window loaded!<br />
// Initialize control(s)!<br />
WinSendDlgItemMsg(hwndDlg, EF_PASSWORD,<br />
EM_SETTEXTLIMIT,<br />
(MPARAM) (iPwdBuffSize-1),<br />
(MPARAM) 0);<br />
WinSetDlgItemText(hwndDlg, EF_PASSWORD, "");<br />
<br />
// Center dialog in screen<br />
if (WinQueryWindowPos(hwndDlg, &swpDlg))<br />
if (WinQueryWindowPos(hwnd, &swpParent))<br />
{<br />
// Center dialog box within the screen<br />
int ix, iy;<br />
ix = swpParent.x + (swpParent.cx - swpDlg.cx)/2;<br />
iy = swpParent.y + (swpParent.cy - swpDlg.cy)/2;<br />
WinSetWindowPos(hwndDlg, HWND_TOP, ix, iy, 0, 0,<br />
SWP_MOVE);<br />
}<br />
WinSetWindowPos(hwndDlg, HWND_TOP, 0, 0, 0, 0,<br />
SWP_SHOW | SWP_ACTIVATE | SWP_ZORDER);<br />
<br />
// Process the dialog!<br />
rc = WinProcessDlg(hwndDlg);<br />
<br />
if (rc!=PB_OK)<br />
{<br />
// The user pressed cancel!<br />
rc = SSMODULE_ERROR_USERPRESSEDCANCEL;<br />
} else<br />
{<br />
// The user pressed OK!<br />
// Get the entered password<br />
WinQueryDlgItemText(hwndDlg, EF_PASSWORD,<br />
iPwdBuffSize,<br />
pchPwdBuff);<br />
rc = SSMODULE_NOERROR;<br />
}<br />
<br />
// Destroy window<br />
WinDestroyWindow(hwndDlg);<br />
<br />
// Hide mouse pointer again, if we're screensaving.<br />
if (!bOnlyPreviewMode)<br />
WinShowPointer(HWND_DESKTOP, FALSE);<br />
<br />
return (MRESULT) rc;<br />
}<br />
<br />
case WM_SHOWWRONGPASSWORD:<br />
// Show mouse pointer, if we're screensaving.<br />
if (!bOnlyPreviewMode)<br />
WinShowPointer(HWND_DESKTOP, TRUE);<br />
<br />
hwndDlg = WinLoadDlg(hwnd, hwnd,<br />
fnAutoHiderDlgProc,<br />
hmodOurDLLHandle,<br />
DLG_WRONGPASSWORD,<br />
NULL);<br />
if (!hwndDlg)<br />
{<br />
// Could not load dialog window resources!<br />
<br />
if (!bOnlyPreviewMode)<br />
WinShowPointer(HWND_DESKTOP, FALSE);<br />
<br />
return (MRESULT) SSMODULE_ERROR_INTERNALERROR;<br />
}<br />
<br />
// Ok, dialog window loaded!<br />
<br />
// Center dialog in screen<br />
if (WinQueryWindowPos(hwndDlg, &swpDlg))<br />
if (WinQueryWindowPos(hwnd, &swpParent))<br />
{<br />
// Center dialog box within the screen<br />
int ix, iy;<br />
ix = swpParent.x + (swpParent.cx - swpDlg.cx)/2;<br />
iy = swpParent.y + (swpParent.cy - swpDlg.cy)/2;<br />
WinSetWindowPos(hwndDlg, HWND_TOP, ix, iy, 0, 0,<br />
SWP_MOVE);<br />
}<br />
WinSetWindowPos(hwndDlg, HWND_TOP, 0, 0, 0, 0,<br />
SWP_SHOW | SWP_ACTIVATE | SWP_ZORDER);<br />
<br />
// Process the dialog!<br />
rc = WinProcessDlg(hwndDlg);<br />
<br />
// Destroy window<br />
WinDestroyWindow(hwndDlg);<br />
<br />
// Hide mouse pointer again, if we're screensaving.<br />
if (!bOnlyPreviewMode)<br />
WinShowPointer(HWND_DESKTOP, FALSE);<br />
<br />
return (MRESULT) SSMODULE_NOERROR;<br />
<br />
To be good players, the SSModule_PauseSaving() and SSModule_ResumeSaving() functions are also implemented. They are the ones which are called when the saver module should temporary stop or resume all of its CPU-intensive tasks, because the monitor has been turned off or turned back on.<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_PauseSaving(void)<br />
{<br />
// This is called when the system wants us to pause saving, because<br />
// a DPMS saving state has been set, and no need for CPU-intensive stuffs.<br />
<br />
if (!bRunning)<br />
return SSMODULE_ERROR_NOTRUNNING;<br />
<br />
return (int) WinSendMsg(hwndSaverWindow, WM_PAUSESAVING, (MPARAM) NULL, (MPARAM) NULL);<br />
}<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_ResumeSaving(void)<br />
{<br />
// This is called when the system wants us to resume saving, because<br />
// a DPMS state has been ended, and we should continue our CPU-intensive stuffs.<br />
<br />
if (!bRunning)<br />
return SSMODULE_ERROR_NOTRUNNING;<br />
<br />
return (int) WinSendMsg(hwndSaverWindow, WM_RESUMESAVING, (MPARAM) NULL, (MPARAM) NULL);<br />
}<br />
<br />
As you can see, it again passes the job to the window procedure, so we have to extend that with two new cases for WM_PAUSESAVING and WM_RESUMESAVING.<br />
<br />
The only CPU-intensive stuff of eCSBall module is the moving of the logo, so, we stop the timer which makes our logo move, when we have to pause saving, and restart the timer when we have to resume saving.<br />
<br />
case WM_PAUSESAVING:<br />
// We should pause ourselves<br />
if (ulAnimationTimerID)<br />
{<br />
WinStopTimer(WinQueryAnchorBlock(hwnd), hwnd,<br />
ulAnimationTimerID);<br />
ulAnimationTimerID = 0; // Note that we're paused!<br />
}<br />
return (MRESULT) SSMODULE_NOERROR;<br />
<br />
case WM_RESUMESAVING:<br />
// We should resume screen saving<br />
if (!ulAnimationTimerID)<br />
{<br />
ulAnimationTimerID = WinStartTimer(WinQueryAnchorBlock(hwnd), hwnd,<br />
ANIMATION_TIMER_ID, // Timer ID<br />
30000); // Let the image have new position every 30 secs<br />
}<br />
return (MRESULT) SSMODULE_NOERROR;<br />
<br />
That's all in general for the eCSBall module. The only remaining parts which are not described here are the RC file (describing the dialog window resources and containing the bitmaps), the makefile and the linker file. All these are available in the ZIP file containing the full source code.<br />
<br />
== Changing the eCSBall module to get the Padlock module ==<br />
<br />
And now the quick part, describing what to replace in this module to get a new module, in which a padlock will move around.<br />
<br />
=== Renaming the files ===<br />
<br />
First of all, it's a good idea to make a copy of the Modules\eCSBall folder, let's name that Modules\Padlock. We should also rename all the files in the new Padlock folder to replace the eCSBall to Padlock in their name.<br />
<br />
=== Modifying the makefile ===<br />
<br />
Then we should modify the makefile, because the name of the source files have been changed.<br />
<br />
There are three lines containing eCSBall, all these have to be replaced to contain Padlock. These lines are:<br />
* dllname=Padlock : This tells the name of the DLL we want to get, and the name of the linker file to get this DLL (the linker file is Padlock.lnk in this case).<br />
* object_files=Padlock.obj : This is a list of object files the DLL will be linked from. In our case, this list consists of only one object file, the Padlock.obj, which will be created by the compiler by compiling the Padlock.c file.<br />
* rcname=Padlock-Resources : This tells the name of the RC file we have to pass on to the resource compiler to compile and add to the final DLL. This resourc file contains the dialog window descriptions and the name of the padlock BMP file.<br />
<br />
=== Modifying the linker file ===<br />
<br />
The linker file (Padlock.lnk) also has to be modified, because it contains a list of object files to link together, and it also contains the internal name of the DLL. So, you should change every eCSBall string to Padlock in the linker file.<br />
<br />
It's also a good idea to change the DESCRIPTION line too, describing the DLL file (this string will be appended to the end of the DLL in clear text form by the linker).<br />
<br />
For your information, the FILE line(s) of the linker file tell the linker the object files to link together, and the NAME line of the linker file tells the linker the internal module name of the DLL file.<br />
<br />
=== Modifying the resources ===<br />
<br />
We want to replace the eCS logo with our padlock image. To do this, the padlock image (Padlock.BMP) has to be copied into this directory, and the Logo*.BMP files can be deleted, as we don't need them anymore.<br />
<br />
Also, we have to replace every link to the old bitmap files with a link to the new padlock image file.<br />
<br />
We'll not need two bitmap IDs, so <br />
* Delete the ''#define BM_ECSBALL2 265'' line from the Padlock-Resources.h file<br />
* Rename the BM_ECSBALL to BM_PADLOCK in the Padlock-Resources.h file<br />
<br />
Now, it's time to tell the resource compiler that we want it to include the Padlock.BMP file into the final binary file, not the logo bitmaps. This can be done by modifying the Padlock-Resources.rc file:<br />
<br />
* Change the name of the resource ID include file to Padlock-Resources.h from eCSBall-Resources.h<br />
* Delete the BITMAP BM_ECSBALL2 "<path and filename>" entry from the end of the file<br />
* Change the BITMAP BM_ECSBALL "<path and filename>" entry to BITMAP BM_PADLOCK "Padlock.BMP"<br />
* Delete the CONTROL BM_ECSBALL2... entry of the RC file<br />
* Change the CONTROL BM_ECSBALL, BM_ECSBALL, 14,... line to CONTROL BM_PADLOCK, BM_PADLOCK, 14,...<br />
<br />
If all this is done, you've successfully renamed the resource files to Padlock, and modified it to have only one bitmap instead of two, and that bitmap will be the Padlock.BMP file.<br />
<br />
=== Modifying the source code ===<br />
<br />
And now we should modify the source code.<br />
<br />
First of all, our resource IDs are not in eCSBall-Resources.h anymore, so we have to change the #include statement at the beginning of the Padlock.c file to include the new Padlock-Resources.h instead.<br />
<br />
It's also a good idea to have a unique window class name for every saver module, so the #define of SAVERWINDOW_CLASS should be changed to this:<br />
<br />
// Private class name for our saver window<br />
#define SAVERWINDOW_CLASS "PADLOCK_SCREENSAVER_CLASS"<br />
<br />
Now, we can go for the real changes.<br />
<br />
First of all, change the SSModule_GetModuleDesc() function to report new information about us:<br />
<br />
SSMODULEDECLSPEC int SSMODULECALL SSModule_GetModuleDesc(SSModuleDesc_p pModuleDesc)<br />
{<br />
if (!pModuleDesc)<br />
return SSMODULE_ERROR_INVALIDPARAMETER;<br />
<br />
// Return info about module!<br />
pModuleDesc->iVersionMajor = 1;<br />
pModuleDesc->iVersionMinor = 20;<br />
strcpy(pModuleDesc->achModuleName, "Bouncing padlock");<br />
strcpy(pModuleDesc->achModuleDesc,<br />
"A golden padlock bouncing around the screen.\n"<br />
"Written by Doodle"<br />
);<br />
<br />
pModuleDesc->bConfigurable = FALSE;<br />
pModuleDesc->bSupportsPasswordProtection = TRUE;<br />
<br />
return SSMODULE_NOERROR;<br />
}<br />
<br />
The other exported SSModule functions can stay as they are. The only thing to modify is the window procedure.<br />
<br />
The window procedure needs the following changes:<br />
* Speed up timer, so it will send WM_TIMER messages at a much faster rate. This will enable us to change the position of the image much faster, instead of changing it once ever 30 seconds.<br />
* When processing the WM_PAINT message, and a new image position is required, we should calculate the new image position from current image position and current image speed, instead of randomly selecting a new position.<br />
<br />
That's all we need.<br />
<br />
Speeding up the timer is quite easy. We only have to change the parameter of WinStartTimer() from 30000 to for example 64. The only thing to remember is that we have to change it in the WM_RESUMESAVING case too, not just at initialization time.<br />
<br />
Changing the function to calculate new image position doesn't have any trick either. A new variable has been introduced, which contains the X and Y speed of the image, and the image position is always modified by these amounts. Once the image reaches one side of the screen, the given image speed is changed to something else, and everything can go on.<br />
<br />
=== Compiling, testing, and enjoying the result ===<br />
<br />
The only thing to do now is to run "wmake" in the Padlock folder. If all goes well, that should build the new module, which can be tested with the tester application, which can be found in the Tester folder.<br />
<br />
If you've created a new module, '''always''' test it with the tester application before trying it with the screen saver core. If you make some mistakes, and the DLL crashes, it can take down the whole WPS, because the screen saver runs in the context of the WPS. It's better to test it with Tester first, so if something goes wrong, it's only the tester which crashes, not the WPS. Once everything goes well, it's safe to copy the DLL to the \OS2\APPS\SSAVER directory, and enjoy it. :)<br />
<br />
== Conclusion ==<br />
<br />
We've went through the internal structure of the eCSBall saver module, and we've seen how to implement all the required functions. We've also seen that it's quite easy to change the module once the developer knows the internal structure of the module, and the new module is not too different from the old one.</div>Doodlehttp://www.edm2.com/index.php/Main_PageMain Page2005-01-10T12:47:56Z<p>Doodle: Added link to article 'Creating modules for Doodle's Screen Saver - Part 1'</p>
<hr />
<div>'''Welcome to the relaunched EDM/2 site!'''<br />
<br />
It's about five years ago that the last issue of EDM/2 was released to the public, since then no new articles about OS/2 and eCS programming appeared on EDM/2. So it was about time to finaly change that! ([[EDM2:About|A short history of EDM/2]])<br />
<br />
For sure we had to face the facts too, the OS/2 and eCS developer community shrank quite a lot so monthly issues are simply impossible nowadays. It would just be a lot of work for the editor and we seriously doubt that we would find enough articles for an ''issue''. If we wait each time to fill a new issue we would not release written articles to the public for a too long time so we decided to go another way that popped up: [http://en.wikipedia.org/wiki/Wiki Wiki]<br><br />
<br />
This basically means that everyone of you out there can contribute to the new EDM/2 without a lot of interaction of other people and even better, the community can extend the articles and fix problems!<br />
<br />
So far, enjoy the new stuff and don't forget to [[contribute]]!<br />
<br />
The new EDM/2 staff<br />
<br />
(aka the crew from [http://www.netlabs.org/ netlabs.org] :)<br />
<br />
<table><br />
<tr style="vertical-align:top;"><br />
<td style="width:50%; width:50%; border: 1px solid #ffc9c9; padding: .5em 1em 1em; color: #000000; background-color: #fff3f3"><br />
<h3>Latest articles</h3><br />
* [[Creating modules for Doodle's Screen Saver - Part 1]] by [[Doodle]]<br />
* [[How to setup old Borland C for use with eComStation Toolkit]] by [[Anakor]]<br />
* [[Porting from Unix platforms]] by [[Ydario]]<br />
* [[Introduction to SOM]] by [[Prokushev]]<br />
<br />
<h3>Republished articles</h3><br />
* [[Progress1|A Progress-indicating Status Line in C++ - Part 1/3]] by [[Stefan Ruck]] (June 1996)<br />
* [[Progress2|A Progress-indicating Status Line in C++ - Part 2/3]] by [[Stefan Ruck]] (July 1996)<br />
* [[Progress3|A Progress-indicating Status Line in C++ - Part 3/3]] by [[Stefan Ruck]] (August 1996)<br />
* [[Manage Your Configuration Files and Data]] by [[Stefan Ruck]] (September 1997)<br />
* [[Multilingual Resources]] by [[Stefan Ruck]] (November 1997)<br />
* [[Let's Talk About... Singleton]] by [[Stefan Ruck]] (December 1997)<br />
* [[Mnemonics in Dialog Boxes]] by [[Stefan Ruck]] (September 1998)<br />
* [[A Keystroke Recorder]] by [[Stefan Ruck]] (January 1999)<br />
* [[Singletons - The Next Generation]] by [[Stefan Ruck]] (September 1999)<br />
* [[OS/2 Frequently Asked Questions]] by [[Dean Roddey]] (November 1995)<br />
* [[Stupid Enumeration Tricks]] by [[Dean Roddey]] (June 1996) <br />
* [[Controlling Yourself. A Framework for Configurable Options]] by [[John Holt]] (December 1994) <br />
* [[Gearing Up For Games]] by [[Michael T. Duffy]] (June 1995)<br />
* [[Gearing Up For Games - Part 2]] by [[Michael T. Duffy]] (August 1995)<br />
* [[Gearing Up For Games - Part 3]] by [[Michael T. Duffy]] (September 1995)<br />
* [[Customizing the Enhanced Editor]] by [[Jörg Schwieder]] (November 1993) <br />
* [[Adding BLDLEVEL information to executables]] by [[Roman Stangl]] (June 1997)<br />
* [[Fitting a Notebook into a Dialog]] by [[Roman Stangl]] (July 1997)<br />
* [[Notebook Key Processing]] by [[Roman Stangl]] (August 1997)<br />
* [[Rebooting OS/2]] by [[Roman Stangl]] (September 1997)<br />
* [[Disabling a Window (Rectangle)]] by [[Roman Stangl]] (October 1997)<br />
* [[Managing DOS Settings]] by [[Roman Stangl]] (July 1998)<br />
* [[Calling PM from AVIO Applications]] by [[Roman Stangl]] (August 1998)<br />
* [[Using SYSLEVEL Files in Your Applications]] by [[Martin Lafaix]] (May 1994)<br />
* [[Resources and Decompiling Them]] by [[Martin Lafaix]] (June 1994)<br />
* [[KEYBOARD.DCP File Format]] by [[Martin Lafaix]] (March 1995)<br />
* [[Implementing Flyout Menus in OS/2]] by [[Mike La Martina]] (May 1999)<br />
* [[OS/2's Symmetrical Multiprocessing Demystified]] by [[Ted Waldron III]] (September 1997)<br />
* [[The Anon CVS Bazaar - Part I]] by [[Henry Sobotka]] (April 1999)<br />
* [[The Anon CVS Bazaar - Part II]] by [[Henry Sobotka]] (May 1999)<br />
* [[A Warped RCS/CVS HowTo]] by [[Henry Sobotka]] (May 1999)<br />
* [[OS/2 Warp Game Developer's Seminar]] by [[Dave Briccetti]] (September 1995)<br />
* [[Sending Mail with REXX]] by [[Dave Briccetti]] (December 1995) <br />
<br />
<br />
<br />
</td><br />
<td style="border: 1px solid #c6c9ff; padding: .5em 1em 1em; color: #000000; background-color: #f0f0ff"><br />
<h3>Categories</h3><br />
* [[SOM]] - WPS, SOM, DSOM, OpenDoc <br />
* [[Driver]] - Device Drivers, IFS<br />
* [[Scripting]] - REXX, DLLs for use with REXX and other scripting stuff <br />
* [[Multimedia]] - Media players, IOCodecs, Graphics stuff<br />
* [[Languages]] - OS/2 and eCS related information for programming languages<br />
* [[Documentation]] - a collection of other ressources like online books, articles etc...<br />
* [[Porting]] - porting applications from Unix/X11/Windows...<br />
<h3>General Stuff</h3><br />
* our [[Mailinglist]] for EDM/2, <br />
* Authors and [[contributors]]<br />
* the [http://www.edm2.com/indexold.html original EDM/2 pages], including the [http://www.edm2.com/common/metaindex.html Meta Index]<br />
* information about how you can [[contribute]]<br />
* some words about the [[license]] of the documents on this page<br />
* translating articles to other [[Contribute#Translation_of_articles|languages]]<br />
<br />
</td><br />
</tr><br />
</table></div>Doodlehttp://www.edm2.com/index.php/Creating_modules_for_Doodle%27s_Screen_Saver_-_Part_1Creating modules for Doodle's Screen Saver - Part 12005-01-10T12:46:37Z<p>Doodle: /* The mandatory functions */</p>
<hr />
<div>== Introduction ==<br />
<br />
The purpose of these articles about ''dssaver'' is to help people to create new screen saver modules for Doodle's Screen Saver. These articles are meant to be something like a tutorial.<br />
<br />
If you want to follow the article, and compile the source code we'll put together here, you should have the source code of ''dssaver'', downloadable from its [http://dssaver.netlabs.org homepage]. You'll not need everything from that package, only the header file for saver modules, called '''SSModule.h'''. However, it's much easier if you take the source code of an already existing saver module, and modify that to fit your needs.<br />
<br />
== What is a saver module? ==<br />
<br />
First things first, let's see what is a screen saver module!<br />
<br />
When a user has ''dssaver'' installed, it's possible to extend that with new modules which will start when ''dssaver'' detects no user activity on the desktop for a given time. It means, that these modules are executable binary files, and they are called by the core of the screen saver, when something has to be done.<br />
<br />
To have the greatest flexibility, these saver modules have to be DLLs, containing specially named functions exported by name.<br />
<br />
Some of these functions are mandatory to be implemented, some are optional. If a DLL contains all the mandatory functions exported, then that DLL is treated as a screen saver module.<br />
<br />
== The screen saver module API ==<br />
<br />
The screen saver module API is defined in the '''SSModule.h''' file. Currently, there are six mandatory functions, and two optional ones. One thing is common though, that all functions return an integer value telling the caller if the function call succeeded or not. These return values are defined in the header file, like SSMODULE_NOERROR, SSMODULE_ERROR_INVALIDPARAMETER, etc...<br />
<br />
=== The mandatory functions ===<br />
<br />
Let's start with the mandatory functions, and let's see them in the order they will be called by the screen saver core!<br />
<br />
When the user goes to the screen saver settings page, the screen saver core creates a list of available saver modules, and shows that list to the user.<br />
<br />
For this, it looks for all the DLL files at C:\OS2\APPS\SSAVER or whatever the bootdrive is, and creates a list from the DLLs having all the mandatory saver functions exported.<br />
<br />
Then, when the user selects one of the modules, the screen saver core has to show some information about the selected module. This is done by calling this function of the DLL:<br />
<br />
int SSModule_GetModuleDesc(SSModuleDesc_p pModuleDesc);<br />
<br />
This function has to fill the pModuleDesc structure with information about the module. This structure is declared this way:<br />
<br />
typedef struct SSModuleDesc_s<br />
{<br />
int iVersionMajor;<br />
int iVersionMinor;<br />
<br />
char achModuleName[SSMODULE_MAXNAMELEN];<br />
char achModuleDesc[SSMODULE_MAXDESCLEN];<br />
<br />
int bConfigurable;<br />
int bSupportsPasswordProtection;<br />
} SSModuleDesc_t, *SSModuleDesc_p;<br />
<br />
The saver module has to tell its version number in iVersionMajor and iVersionMinor. One thing to be noted about it is that the minor version number will be padded with a leading zero, if smaller then 10, so if your module version is e.g. v1.5, then you should set iVersionMajor to 1, and iVersionMinor to 50.<br />
<br />
The achModuleName has to contain a zero terminated string, telling the short name of the module. This is the name which will be shown in the list of modules.<br />
<br />
The achModuleDesc tells the long description of the module. It can contain line-breaks too, as it is the string which will be shown in a MLE at the settings page.<br />
<br />
The next two fields are boolean fields, they have to contain 1 or 0 (as TRUE or FALSE). They are informational fields, telling the screen saver core if it should enable the "''Configure''" button for this module or not (bConfigurable field), and telling the user if the module supports password protection or not (bSupportsPasswordProtection field).<br />
<br />
If the user selects this module from the list of available saver modules, the screen saver core shows this information to the user. When the user presses the "''Configure''" button there (provided that it was enabled, because the bConfigurable field was TRUE), the screens saver calls the next function of the module:<br />
<br />
int SSModule_Configure(HWND hwndOwner, char *pchHomeDirectory);<br />
<br />
This function should either return SSMODULE_ERROR_NOTSUPPORTER, or it should enable the user to configure the saver module in some way, and return SSMODULE_NOERROR.<br />
<br />
This configuration is usually done by a module-specific dialog window. If the module creates that window, it has to do it so that the owner of that window will be ''hwndOwner'', so the screen saver settings dialog will be disabled while the user is configuring the saver module.<br />
<br />
The pchHomeDirectory parameter contains a string, the full path (with a trailing backslash!) of the screen saver home directory, where the modules and config files are located. It is usually C:\OS2\APPS\SSAVER, but nothing tells that it can not be changed later.<br />
<br />
This directory can be used to store the private configuration file of the module, but it's not necessary to use it, once can use the OS2.INI file too, to save the configuration of the module.<br />
<br />
So, this function should load the current module configuration (from the given directory or from anywhere), show a dialog window, then save the new configuration.<br />
<br />
When it's needed to start the screen saver module for some reason, the screen saver core calls the following function:<br />
<br />
int SSModule_StartSaving(HWND hwndParent, char *pchHomeDirectory, int bPreviewMode);<br />
<br />
This function is used both for starting the saver module for the preview window, and for starting the module to do the real screen saving.<br />
<br />
What this function do is completely left for the developer of the module. It all depends on what the module wants to do to save the screen.<br />
<br />
Of course, most of the time it's that the module will create a new window, and do something nice and attractive in that window, but it can be that there is no need for such a window, because the module communicates with an external device to turn off the monitor, or to send an SMS to somebody that the user of the computer is in idle. :)<br />
<br />
However, you have to note something. This function has to start the screen saving, and has to return then. It can not create a window and go to a message loop here. It means, that the best way is to start a new thread from this function, which will set up a new window and which will include the message loop for that window, and if that thread succeeds in creating the window, return with SSMODULE_NOERROR, otherwise return with an error code.<br />
<br />
The third parameter of this function (bPreviewMode) tells if the saving should be started in a small window, as a preview window (bPreviewMode is TRUE in this case), or for real screen saving (bPreviewMode is FALSE in this case).<br />
<br />
If the saver has to be started as a preview window, then the hwndParent contains a window handle, which has to be the parent of the saver preview window. This window is the white rectangle with the "No preview" text inside, when the preview is turned off at the settings page of the screen saver. The best way to do the preview window is to subclass this window for the time of previewing.<br />
<br />
If the saver has to be started for real screen saving, then hwndParent contains HWND_DESKTOP, and a new window should be created. To make sure that no other windows will be able to draw on top of the saver window, this new window should be made system modal and always on top.<br />
<br />
So, to summarize what to do in this function, you should:<br />
* Load the configuration of the module (from pchHomeDirectory or from INI files).<br />
* Start a new thread to do the saving. This new thread will either subclass hwndParent (when bPreviewMode is TRUE), or create a new system modal, always on top window (when bPreviewMode is FALSE).<br />
* Wait for that thread, to see if it could create the window.<br />
* Return success or failure<br />
<br />
Once the screen saving is going on, and the user does some kind of activity, the screen saving should be turned off. To notify the saver module to stop the screen saving (or the preview window), the screen saver core calls the following function:<br />
<br />
int SSModule_StopSaving(void);<br />
<br />
This is the function which should undo everything that SSModule_StartSaving() did.<br />
<br />
It usually means, that it shoul notify the new thread created by the other function to stop saving, and wait for it to be done. That other thread then can clean up everything it did, and terminate.<br />
<br />
If the theme of the screen saver module enables, it can be done to have a short "lead-out" when it's asked to stop the saving, but make sure it won't make be too long, or the users will hate it otherwise. Also, it's good not to have these lead-outs when the saver runs in preview mode, because that would make switching from one module to another quite time consuming.<br />
<br />
If the password protection is enabled in the screen saver core, then it will make sure that the user knows the right password before stopping the screen saver. So, it will ask password from the user, and compare it to the one set for screen saving. If they match, then the screen saver module will be stopped by calling SSModule_StopSaving(), but if they don't match, it will be told to the user, and the saver module will continue running, it won't be stopped.<br />
<br />
To make it possible to have a password asking window which fits into the look of the current screen saver, the asking of the password is done by the screen saver modules, when the screen saver core calls this function:<br />
<br />
int SSModule_AskUserForPassword(int iPwdBuffSize, char *pchPwdBuff);<br />
<br />
So, when this function of the saver module is called, the module should somehow get a password from the user.<br />
<br />
Usually, it's done by showing a dialog window, an letting the user enter the password there, but it can be done in any other way, too. For example, when a running saver module gets this call, it could have an entry field flowing in from the left side to the middle of the screen, and waiting there for 10 seconds, to let the user enter the password. If the user doesn't enter it, or presses Esc, it could flow out to the right side, and this function should return SSMODULE_ERROR_USERPRESSEDCANCEL, indicating that no password has been entered. Otherewise, it should return the password in pchPwdBuff (where the buffer itself has a maximum size of iPwdBuffSize), and return SSMODULE_NOERROR.<br />
<br />
One thing to note here is that you should always make timeout for these methods, so it won't happen that the password asking window comes up accidentally, and stays there forever, which would not really save the screen at the position of the password asking window.<br />
<br />
If a module doesn't support password protection, it has to return SSMODULE_ERROR_NOTSUPPORTED. This will tell the screen saver core that there is no way to get a password from the user, so the core will simply not ask password, but stop the screen saver module, even if the password protection was turned on.<br />
<br />
The password returned by this function will be compared by the screen saver core to the password set by the user. If the passwords match, the saver module will be stopped. But if the passwords do not match, the following function of the saver module will be called:<br />
<br />
int SSModule_ShowWrongPasswordNotification(void);<br />
<br />
This should notify the user that the password he entered is wrong.<br />
<br />
Again, this is usually done by a simple dialog window, but it can be done in any other way, too, possibly in more attractive ways, fitting into the look of the saver module.<br />
<br />
The same applies here, which applied to the previous function: Have a timeout for this notification. Don't let this window kill the screen saving.<br />
<br />
This function should return only when the password notification has been done.<br />
<br />
These were the mandatory functions, required to be implemented by every saver module.<br />
<br />
=== The optional functions ===<br />
<br />
The following function are optional, they don't have to be implemented, but they help a lot if they are.<br />
<br />
These functions have been implemented to support the DPMS states. When the screen saver core switches to a given DPMS state (e.g. Standby state), the monitor will go black, so there is no need for the saver to eat that much CPU it eats with updating the screen and calculating the new frames to be shown. For this reason, the following two functions of the saver module will be called by the screen saver core (if they exist, and exported by the current module), to temporary pause the saver module, and to resume its running later on.<br />
<br />
int SSModule_PauseSaving(void);<br />
<br />
This function should pause the CPU intensive tasks of the screen saver module. For example, it pauses the DIVE blitter thread in IFSIM module, so it will stop calculating the next frames, and it will not blit the frames to the screen anymore. This, effectively, stops every CPU activity of the module.<br />
<br />
int SSModule_ResumeSaving(void);<br />
<br />
This function should undo everything done in SSModule_PauseSaving(). It is called if SSModule_PauseSaving() was called before, and the user presses a key or moves the mouse. In the IFSIM module, it re-enables the DIVE blitter thread, so that will continue calculating new frames and showing them on the screen.<br />
<br />
== Conclusion ==<br />
<br />
We've went through the screen saver module API in this part of the article. We've seen that the saver modules have to be DLLs, and we've seen what functions it has to implement to become a valid screen saver module.<br />
<br />
In the next part, we'll go through the steps of creating a simple saver module.</div>Doodlehttp://www.edm2.com/index.php/Creating_modules_for_Doodle%27s_Screen_Saver_-_Part_1Creating modules for Doodle's Screen Saver - Part 12005-01-10T12:43:58Z<p>Doodle: </p>
<hr />
<div>== Introduction ==<br />
<br />
The purpose of these articles about ''dssaver'' is to help people to create new screen saver modules for Doodle's Screen Saver. These articles are meant to be something like a tutorial.<br />
<br />
If you want to follow the article, and compile the source code we'll put together here, you should have the source code of ''dssaver'', downloadable from its [http://dssaver.netlabs.org homepage]. You'll not need everything from that package, only the header file for saver modules, called '''SSModule.h'''. However, it's much easier if you take the source code of an already existing saver module, and modify that to fit your needs.<br />
<br />
== What is a saver module? ==<br />
<br />
First things first, let's see what is a screen saver module!<br />
<br />
When a user has ''dssaver'' installed, it's possible to extend that with new modules which will start when ''dssaver'' detects no user activity on the desktop for a given time. It means, that these modules are executable binary files, and they are called by the core of the screen saver, when something has to be done.<br />
<br />
To have the greatest flexibility, these saver modules have to be DLLs, containing specially named functions exported by name.<br />
<br />
Some of these functions are mandatory to be implemented, some are optional. If a DLL contains all the mandatory functions exported, then that DLL is treated as a screen saver module.<br />
<br />
== The screen saver module API ==<br />
<br />
The screen saver module API is defined in the '''SSModule.h''' file. Currently, there are six mandatory functions, and two optional ones. One thing is common though, that all functions return an integer value telling the caller if the function call succeeded or not. These return values are defined in the header file, like SSMODULE_NOERROR, SSMODULE_ERROR_INVALIDPARAMETER, etc...<br />
<br />
=== The mandatory functions ===<br />
<br />
Let's start with the mandatory functions, and let's see them in the order they will be called by the screen saver core!<br />
<br />
When the user goes to the screen saver settings page, the screen saver core creates a list of available saver modules, and shows that list to the user.<br />
<br />
For this, it looks for all the DLL files at C:\OS2\APPS\SSAVER or whatever the bootdrive is, and creates a list from the DLLs having all the mandatory saver functions exported.<br />
<br />
Then, when the user selects one of the modules, the screen saver core has to show some information about the selected module. This is done by calling this function of the DLL:<br />
<br />
int SSModule_GetModuleDesc(SSModuleDesc_p pModuleDesc);<br />
<br />
This function has to fill the pModuleDesc structure with information about the module. This structure is declared this way:<br />
<br />
typedef struct SSModuleDesc_s<br />
{<br />
int iVersionMajor;<br />
int iVersionMinor;<br />
<br />
char achModuleName[SSMODULE_MAXNAMELEN];<br />
char achModuleDesc[SSMODULE_MAXDESCLEN];<br />
<br />
int bConfigurable;<br />
int bSupportsPasswordProtection;<br />
} SSModuleDesc_t, *SSModuleDesc_p;<br />
<br />
The saver module has to tell its version number in iVersionMajor and iVersionMinor. One thing to be noted about it is that the minor version number will be padded with a leading zero, if smaller then 10, so if your module version is e.g. v1.5, then you should set iVersionMajor to 1, and iVersionMinor to 50.<br />
<br />
The achModuleName has to contain a zero terminated string, telling the short name of the module. This is the name which will be shown in the list of modules.<br />
<br />
The achModuleDesc tells the long description of the module. It can contain line-breaks too, as it is the string which will be shown in a MLE at the settings page.<br />
<br />
The next two fields are boolean fields, they have to contain 1 or 0 (as TRUE or FALSE). They are informational fields, telling the screen saver core if it should enable the "''Configure''" button for this module or not (bConfigurable field), and telling the user if the module supports password protection or not (bSupportsPasswordProtection field).<br />
<br />
If the user selects this module from the list of available saver modules, the screen saver core shows this information to the user. When the user presses the "''Configure''" button there (provided that it was enabled, because the bConfigurable field was TRUE), the screens saver calls the next function of the module:<br />
<br />
int SSModule_Configure(HWND hwndOwner, char *pchHomeDirectory);<br />
<br />
This function should either return SSMODULE_ERROR_NOTSUPPORTER, or it should enable the user to configure the saver module in some way, and return SSMODULE_NOERROR.<br />
<br />
This configuration is usually done by a module-specific dialog window. If the module creates that window, it has to do it so that the owner of that window will be ''hwndOwner'', so the screen saver settings dialog will be disabled while the user is configuring the saver module.<br />
<br />
The pchHomeDirectory parameter contains a string, the full path (with a trailing backslash!) of the screen saver home directory, where the modules and config files are located. It is usually C:\OS2\APPS\SSAVER, but nothing tells that it can not be changed later.<br />
<br />
This directory can be used to store the private configuration file of the module, but it's not necessary to use it, once can use the OS2.INI file too, to save the configuration of the module.<br />
<br />
So, this function should load the current module configuration (from the given directory or from anywhere), show a dialog window, then save the new configuration.<br />
<br />
When it's needed to start the screen saver module for some reason, the screen saver core calls the following function:<br />
<br />
int SSModule_StartSaving(HWND hwndParent, char *pchHomeDirectory, int bPreviewMode);<br />
<br />
This function is used both for starting the saver module for the preview window, and for starting the module to do the real screen saving.<br />
<br />
What this function do is completely left for the developer of the module. It all depends on what the module wants to do to save the screen.<br />
<br />
Of course, most of the time it's that the module will create a new window, and do something nice and attractive in that window, but it can be that there is no need for such a window, because the module communicates with an external device to turn off the monitor, or to send an SMS to somebody that the user of the computer is in idle. :)<br />
<br />
However, you have to note something. This function has to start the screen saving, and has to return then. It can not create a window and go to a message loop here. It means, that the best way is to start a new thread from this function, which will set up a new window and which will include the message loop for that window, and if that thread succeeds in creating the window, return with SSMODULE_NOERROR, otherwise return with an error code.<br />
<br />
The third parameter of this function (bPreviewMode) tells if the saving should be started in a small window, as a preview window (bPreviewMode is TRUE in this case), or for real screen saving (bPreviewMode is FALSE in this case).<br />
<br />
If the saver has to be started as a preview window, then the hwndParent contains a window handle, which has to be the parent of the saver preview window. This window is the white rectangle with the "No preview" text inside, when the preview is turned off at the settings page of the screen saver. The best way to do the preview window is to subclass this window for the time of previewing.<br />
<br />
If the saver has to be started for real screen saving, then hwndParent contains HWND_DESKTOP, and a new window should be created. To make sure that no other windows will be able to draw on top of the saver window, this new window should be made system modal and always on top.<br />
<br />
So, to summarize what to do in this function, you should:<br />
- Load the configuration of the module (from pchHomeDirectory or from INI files).<br />
- Start a new thread to do the saving. This new thread will either subclass hwndParent (when bPreviewMode is TRUE), or create a new system modal, always on top window (when bPreviewMode is FALSE).<br />
- Wait for that thread, to see if it could create the window.<br />
- Return success or failure<br />
<br />
Once the screen saving is going on, and the user does some kind of activity, the screen saving should be turned off. To notify the saver module to stop the screen saving (or the preview window), the screen saver core calls the following function:<br />
<br />
int SSModule_StopSaving(void);<br />
<br />
This is the function which should undo everything that SSModule_StartSaving() did.<br />
<br />
It usually means, that it shoul notify the new thread created by the other function to stop saving, and wait for it to be done. That other thread then can clean up everything it did, and terminate.<br />
<br />
If the theme of the screen saver module enables, it can be done to have a short "lead-out" when it's asked to stop the saving, but make sure it won't make be too long, or the users will hate it otherwise. Also, it's good not to have these lead-outs when the saver runs in preview mode, because that would make switching from one module to another quite time consuming.<br />
<br />
If the password protection is enabled in the screen saver core, then it will make sure that the user knows the right password before stopping the screen saver. So, it will ask password from the user, and compare it to the one set for screen saving. If they match, then the screen saver module will be stopped by calling SSModule_StopSaving(), but if they don't match, it will be told to the user, and the saver module will continue running, it won't be stopped.<br />
<br />
To make it possible to have a password asking window which fits into the look of the current screen saver, the asking of the password is done by the screen saver modules, when the screen saver core calls this function:<br />
<br />
int SSModule_AskUserForPassword(int iPwdBuffSize, char *pchPwdBuff);<br />
<br />
So, when this function of the saver module is called, the module should somehow get a password from the user.<br />
<br />
Usually, it's done by showing a dialog window, an letting the user enter the password there, but it can be done in any other way, too. For example, when a running saver module gets this call, it could have an entry field flowing in from the left side to the middle of the screen, and waiting there for 10 seconds, to let the user enter the password. If the user doesn't enter it, or presses Esc, it could flow out to the right side, and this function should return SSMODULE_ERROR_USERPRESSEDCANCEL, indicating that no password has been entered. Otherewise, it should return the password in pchPwdBuff (where the buffer itself has a maximum size of iPwdBuffSize), and return SSMODULE_NOERROR.<br />
<br />
One thing to note here is that you should always make timeout for these methods, so it won't happen that the password asking window comes up accidentally, and stays there forever, which would not really save the screen at the position of the password asking window.<br />
<br />
If a module doesn't support password protection, it has to return SSMODULE_ERROR_NOTSUPPORTED. This will tell the screen saver core that there is no way to get a password from the user, so the core will simply not ask password, but stop the screen saver module, even if the password protection was turned on.<br />
<br />
The password returned by this function will be compared by the screen saver core to the password set by the user. If the passwords match, the saver module will be stopped. But if the passwords do not match, the following function of the saver module will be called:<br />
<br />
int SSModule_ShowWrongPasswordNotification(void);<br />
<br />
This should notify the user that the password he entered is wrong.<br />
<br />
Again, this is usually done by a simple dialog window, but it can be done in any other way, too, possibly in more attractive ways, fitting into the look of the saver module.<br />
<br />
The same applies here, which applied to the previous function: Have a timeout for this notification. Don't let this window kill the screen saving.<br />
<br />
This function should return only when the password notification has been done.<br />
<br />
These were the mandatory functions, required to be implemented by every saver module.<br />
<br />
=== The optional functions ===<br />
<br />
The following function are optional, they don't have to be implemented, but they help a lot if they are.<br />
<br />
These functions have been implemented to support the DPMS states. When the screen saver core switches to a given DPMS state (e.g. Standby state), the monitor will go black, so there is no need for the saver to eat that much CPU it eats with updating the screen and calculating the new frames to be shown. For this reason, the following two functions of the saver module will be called by the screen saver core (if they exist, and exported by the current module), to temporary pause the saver module, and to resume its running later on.<br />
<br />
int SSModule_PauseSaving(void);<br />
<br />
This function should pause the CPU intensive tasks of the screen saver module. For example, it pauses the DIVE blitter thread in IFSIM module, so it will stop calculating the next frames, and it will not blit the frames to the screen anymore. This, effectively, stops every CPU activity of the module.<br />
<br />
int SSModule_ResumeSaving(void);<br />
<br />
This function should undo everything done in SSModule_PauseSaving(). It is called if SSModule_PauseSaving() was called before, and the user presses a key or moves the mouse. In the IFSIM module, it re-enables the DIVE blitter thread, so that will continue calculating new frames and showing them on the screen.<br />
<br />
== Conclusion ==<br />
<br />
We've went through the screen saver module API in this part of the article. We've seen that the saver modules have to be DLLs, and we've seen what functions it has to implement to become a valid screen saver module.<br />
<br />
In the next part, we'll go through the steps of creating a simple saver module.</div>Doodlehttp://www.edm2.com/index.php/How_to_setup_old_Borland_C_for_use_with_eComStation_ToolkitHow to setup old Borland C for use with eComStation Toolkit2005-01-10T09:39:01Z<p>Doodle: /* Installing the Toolkit */</p>
<hr />
<div>By [[Anakor]]<br />
<br />
== Introduction ==<br />
<br />
Back in 1994 Borland Int. had released the last version 2.0 of their Borland C/C++ for OS/2 Compiler. It came with OS/2 2.0 header files and documentation. That seems to be very outdated.<br />
<br />
In my opinion the Borland compiler is a good compiler for beginners or small projects. It has a nice GUI and the great Ressource Workshop. But we have to deal with these problems:<br />
<br />
First, the headers had no knowledge about MMOS2, so it isn't possible to use any of the MMOS2 Functions with the original files.<br />
Second, IBM had changed several things in the header files back in Warp 4 days. Since then, the Borland Compiler isn't supported anymore in the headers.<br />
<br />
But believe it or not, where there's a will there's a way.<br />
<br />
== Installing the Toolkit ==<br />
<br />
Our first step to get these working, is to install the toolkit from the eComStation 1.1 Disk 2. The Toolkit comes with an install programm so this thread is pretty simple. After install and reboot is done, your config.sys will be supplied with various SET statements and other stuff.<br />
<br />
For those of you don't want to mess up the config.sys and favor a small installation I recommend to ''not'' use the supplied tkinst program. Instead you should simply copy the required files by hand to a directory of your choice. You should proceed through following steps:<br />
<br />
1. Create a directory tree to place the toolkit files in:<br />
<br />
toolkit ──┐<br />
h<br />
book<br />
help<br />
ipfc<br />
bin<br />
<br />
Create this tree by hand on your HD. We don't need any other directories.<br />
<br />
2. Copy all files and directories available in <source>\h from your toolkit source to your previously created directory <toolkit>\h.<br />
<br />
3. Copy all Onlinebook files available in <source>\book from your toolkit source to <toolkit>\book. This is only the documentation, so this isn't a must.<br />
<br />
4. Copy all files available in <source>\ipfc from your toolkit source to <toolkit>\ipfc. These are help compiler files, so it is required only if you want create help files using the IPF-Compiler.<br />
<br />
5. Copy ipfc.exe in <source>\bin from your toolkit source to <toolkit>\bin. The ipfc executeable is required only if you want create help files.<br />
<br />
6. Check and/or edit your config.sys. Make shure these settings are<br />
available and working:<br />
<br />
SET HELP=<Toolkit>\Help;%HELP%<br><br />
SET BOOKSHELF=<Toolkit>\BOOK;%BOOKSHELF%<br><br />
SET HELPNDX=EPMKWHLP.NDX;%HELPNDX%<br><br />
SET IPFC=<Toolkit>\IPFC;%IPFC%<br><br />
SET CPREF=CP1.INF+CP2.INF+CP3.INF<br><br />
SET GPIREF=GPI1.INF+GPI2.INF+GPI3.INF+GPI4.INF<br><br />
SET MMREF=MMREF1.INF+MMREF2.INF+MMREF3.INF<br><br />
SET PMREF=PM1.INF+PM2.INF+PM3.INF+PM4.INF+PM5.INF<br><br />
SET WPSREF=WPS1.INF+WPS2.INF+WPS3.INF<br><br />
SET PATH=<Toolkit>\Bin;%PATH%<br><br />
<br />
After this all applied and system is rebooted the toolkit files are ready to work with.<br />
<br />
== Installing the BCOS2-Compiler ==<br />
<br />
<P>The original Borland for OS/2 CD-Rom or disks came with an install application. Run it and install the compiler normally. But don't let the Operating System reboot.<br><br />
After the install program terminates, go to your config.sys and remove all settings which the install program has applied there. Especially look for the settings</P><br />
<br />
<P>'''libpath=<bcos2>\bin'''<BR><br />
'''SET IPFC=<bcos2>\ipfc'''<BR><br />
'''SET BOOKSHELF=<bcos2>\bin'''<BR><br />
'''SET HELP=<bcos2>\bin'''</P><br />
<br />
<P>If these statements are at the end of your config.sys it will overwrite our statements from the previous chapter.<br />
Instead of these modifications you should apply this setting:</P><br />
<br />
<P>'''SET PATH=<bcos2>\bin'''</P><br />
<br />
<P>If you consider the use of the Borland GUI controls (required by Ressource Workshop), copy the required DLL from <bcos2>\bin\bpmcc.dll to <bootdrive>:\ecs\dll\bpmcc.dll. In this case all applications wanting this dll will have it.</P><br />
<br />
<P>After this modifications are made, reboot your machine.</P><br />
<br />
== Modifications on Borland C/C++ and the Toolkit ==<br />
<br />
<P>If you compile a program using BCOS2 the compiler links/binds libraries with your own executeable file. But the compiler only has the outdated OS/2 2.0 library, so we need to replace it with something. The file to replace is <bcos2>\lib\os2.lib. The needed files are included in the toolkit\lib directory on the ecomstation 1.1 disk 2.<BR><br />
So you have to copy toolkit\lib\os2386.lib to <bcos2>\lib\os2.lib.</P><br />
<br />
<P>If you want bind MMOS2 functions as well, you have to copy toolkit\lib\mmos2.lib to <bcos2>\lib\mmos2.lib.<BR><br />
Do so for any library you want to link/bind with your program.</P><br />
<br />
<P>To prevent collision between toolkit headers with borland headers, I recommend to delete or move all headers from the <bcos2>\include directory that already is in <toolkit>\h.<BR><br />
'''Make a filename compare and remove them!'''</P><br />
<br />
<P>As I stated in the first chapter, IBM has removed Borland support from the header files in 1995/1996. To get the toolkit headers working with BCOS2 again, we have to edit the file <toolkit>\h\os2def.h.<BR><BR><br />
<br />
The original os2def.h looks like this<BR><BR><br />
<br />
/* NOINC */<BR><br />
<nowiki>#</nowiki>define FAR /* this will be deleted shortly */<BR><br />
<nowiki>#</nowiki>define NEAR /* this will be deleted shortly */<BR><br />
<nowiki>#</nowiki>define APIENTRY _System<BR><br />
<nowiki>#</nowiki>define EXPENTRY _System<BR><br />
<nowiki>#</nowiki>define APIENTRY16 _Far16 _Pascal<BR><br />
<nowiki>#</nowiki>define PASCAL16 _Far16 _Pascal<BR><br />
<nowiki>#</nowiki>define CDECL16 _Far16 _Cdecl<BR><br />
<nowiki>#</nowiki>define VOID void<BR><br />
<BR><br />
The modified os2def.h should look like this:<BR><br />
<BR><br />
/* NOINC */<BR><br />
<nowiki>#</nowiki>define FAR /* this will be deleted shortly */<BR><br />
<nowiki>#</nowiki>define NEAR /* this will be deleted shortly */<BR><br />
<BR><br />
<nowiki>#</nowiki>if defined(__IBMC__) || defined(__IBMCPP__)<BR><br />
<nowiki>#</nowiki>define APIENTRY _System<BR><br />
<nowiki>#</nowiki>define EXPENTRY _System<BR><br />
<nowiki>#</nowiki>endif<BR><br />
<BR><br />
<nowiki>#</nowiki>if defined(__BORLANDC__)<BR><br />
<nowiki>#</nowiki>define _Far16 _far16<BR><br />
<nowiki>#</nowiki>define _Pascal _pascal<BR><br />
<nowiki>#</nowiki>define APIENTRY __syscall<BR><br />
<nowiki>#</nowiki>define EXPENTRY __syscall<BR><br />
<nowiki>#</nowiki>define FAR16PTR _Far16 *<BR><br />
<nowiki>#</nowiki>define _Seg16<BR><br />
<nowiki>#</nowiki>endif<BR><br />
<BR><br />
<nowiki>#</nowiki>define APIENTRY16 _Far16 _Pascal<BR><br />
<nowiki>#</nowiki>define PASCAL16 _Far16 _Pascal<BR><br />
<nowiki>#</nowiki>define CDECL16 _Far16 _Cdecl<BR><br />
<BR><br />
<nowiki>#</nowiki>define VOID void<BR><br />
<br />
== Conclusion ==<br />
<br />
[[Image:bcos2_dirs.png]]<br />
To test the installation, we have to create a project file with our sources. As you see in the image the paths are set to both, the toolkit and the BCOS2 ressources, but the toolkit files first.<br />
<br />
I hope this info is helpful to anybody.<br />
<br />
== Additional Information ==<br />
<br />
[http://www.edm2.com/0308/compilers.html C++ Compiler Review] by Gordon Zeglinski<BR><br />
[http://www.it-frame.de IT-Frame]<BR><br />
[http://www.anakor.de anakor.de]<BR></div>Doodlehttp://www.edm2.com/index.php/Help:EditingHelp:Editing2005-01-10T09:36:42Z<p>Doodle: Copied help from wiki.netlabs.org</p>
<hr />
<div>===Preformatted text===<br />
<br />
This is useful for source code.<br />
Just start each sentence with a space like in the following example.<br />
<br />
<- here is a space<br />
<- here is a space<br />
<- here is a space<br />
<- here is a space<br />
<br />
===Links===<br />
<br />
Links to pages on the internet are created just by writing the URL<br />
<br />
blabla bla bla <nowiki>http://www.netlabs.org</nowiki> bla bla bla...<br />
<br />
as seen here: blabla bla bla http://www.netlabs.org blabla bla...<br />
<br />
If you want to have a named link the syntax is slightly different.<br />
<br />
blabla bla bla <nowiki>[http://www.netlabs.org This links to netlabs.org]</nowiki> bla bla bla...<br />
<br />
blabla bla bla [http://www.netlabs.org This links to netlabs.org] bla bla bla...</div>Doodle