Now What? OSS Personal Pascal and the beginner - part 2 written by David Meile [Copyright 1986 David Meile, all rights reserved. Permission is given to Atari user groups to reprint this article, as long as this statement is included. OSS Personal Pascal is a product of Optimized Systems Software, Inc. I am not related to the company, I simply bought their compiler.] Windows, Graphics and Pascal One of the many nice things about OSS Personal Pascal is that it provides the Pascal programmer with simple access to many of the GEM operating system features. Unfortunately, while access is simple, it is not always EASY. GEM requires a lot of prompting before it will produce something like a Window. But, once you get the basic idea of how to access GEM from Pascal, it DOES become easier as you write more programs. Last time, I said I was going to draw some lines. Actually, I am going to do a little more than that ... I am going to introduce the basics of creating a Window to draw ON. The program that follows is called GRAPHICS.PAS. The program PROGRAM graphics; { Clear the screen and draw something } CONST {$I gemconst.pas } TYPE {$I gemtype.pas } VAR x,y,w,h, my_window : integer; my_event : integer; wind_type : integer; title : Window_Title; { The following must be defined for completeness, but are not used } message : Message_Buffer; key : integer; bcnt,bstate : integer; mx,my : integer; kbd_state : integer; {$I gemsubs.pas } PROCEDURE Do_Graph; BEGIN wind_type := G_Name; title := ' Drawing a box '; my_window := New_Window( wind_type, title, 0, 0, 0, 0 ); Hide_Mouse; Open_Window( my_window, 0, 0, 0, 0 ); Set_Window( my_window ); Work_Rect( my_window, x, y, w, h ); Set_Clip( x, y, w, h ); Paint_Color( White ); Paint_Rect( 0, 0, w, h ); Line_Color( Black ); Text_Color( Black ); Draw_String( 10,10, 'You can draw text on screen too!'); Line( 15,15, 15,100 ); Line_To( 100,100 ); Line_To( 100,15 ); Line_To( 15,15 ); Line_To( 100,100 ); Line( 15,100, 100,15 ); Draw_String( 15,110, 'Click the left button to exit...'); my_event := Get_Event( E_Button, 1, 1, 1, 0, False, 0, 0, 0, 0, False, 0, 0, 0, 0, message, key, bcnt, bstate, mx, my, kbd_state ); Close_Window( my_window ); Show_Mouse; Delete_Window( my_window ); END; BEGIN IF Init_Gem >= 0 THEN BEGIN Do_Graph; Exit_Gem; END; END. And now ... the explanation Quite a lot of things are going on in that little program, right? I am sure that you recognize most of the lines at the very end. There is the 'Init_Gem' function, asking GEM if it can create a new workspace. There is the 'Exit_Gem' procedure, telling GEM that the program is finished with the workspace. And there is that funny line that simply says 'Do_Graph'. But first, a brief digression Pascal works best in small chunks of code, called PROCEDURES and FUNCTIONS. As you may remember, a function is a subprogram that, when called, returns a single value. The function 'Init_Gem' returns an integer value greater or equal to zero when GEM can run the program. A PROCEDURE is a subprogram that, when called, performs some sort of action. It may (or may not) return one or more values. Procedures can require PARAMETERS, which are variables with some sort of value. 'Draw_String' is a procedure in the program above. It's parameters are two integer values and a string. (Remember, strings are enclosed by single quota marks - 'This is a string'). 'Exit_Gem' is a procedure with no parameters. One of the advantages of Pascal over BASIC is that procedures and functions can have variables that do not affect the program outside of that particular subprogram. A short example: Program example1; VAR a, b : integer; Procedure dark( x : integer; y : integer ); VAR a, b : integer; BEGIN {Procedure dark} a := x; b := a * y; Writeln( 'a = ', a , ' b = ', b ); END; {dark} BEGIN {Main program example1} a := 10; b := 5; dark( a, b ); Writeln( 'a = ', a, ' b = ', b ); END. {example 1} What do you think this short program will do? Print out values of 'a' and 'b' of course. First, assign 10 to a and 5 to b. Then call the procedure dark. The two parameters are a and b ... they are called x and y WITHIN procedure dark. Assign x to a (so a = 10) and assign a * y to b (10 * 5 = 50, so b = 50). The Writeln will print out the following: a = 10 b = 50 Now, exit dark and go back to the main program. Here we print out the values of a and b. But the values of a and b have not changed in the main program, they only changed within procedure dark. So, a is STILL 10 and b is STILL 5 in the main program. The Writeln will print out the following: a = 10 b = 5 If you have followed this, congratulations! You now know about LOCAL (within a procedure) variables. Now, back to the REAL program OK. You can see that 'Do_Graph' is a procedure. There are no parameters, so any variables included in Do_Graph must be GLOBAL .. that is, the variables listed at the top of the program apply to the procedure as well. A GLOBAL variable can be changed by ANY subprogram, and should not be used much within your own programs. 'Proper' Pascal programs almost always use LOCAL variables within procedures, passing parameters back to the main program when necessary. With that in mind, let's look at what's going on in the procedure called Do_Graph. The first thing you can see are several assignments. 'wind_type' is an integer variable used in the function New_Window. It can be made up of several parts, and describes the parts of the GEM Window we want to be there. These include the scroll bars, the "close window" box, and the window name. G_Name is an bit value for the window name. G_Close is another bit value for the close window box. When combined they produce an integer value that we can assign to wind_type. To include both, you would type: wind_type := G_Name | G_Close; Additional parts of a GEM window can be included: G_Full, G_Move, G_Info, etc. Since I'm going to have a window name, I may as well tell GEM what it is, right? That is the variable 'title'. You'll notice that the variable is declared 'title : Window_Title'. Window_Title is a special TYPE included in the GEMTYPE.PAS file. If you're interested, you can look at GEMTYPE.PAS to see how "special" types are declared. Having my two variables assigned, I make a call to the function called 'New_Window'. You can tell it is a function because it's on the right side of an assignment statement. The variable 'my_window' is an integer, so the function New_Window returns an integer value. The function New_Window asks GEM to set up space for a window, and lets Pascal know how to access it by number. It also tells GEM what the title for the window should be. I don't want the mouse arrow to clutter up my nice graphics screen, so I tell GEM to hide it with the procedure 'Hide_Mouse'. You MUST include a 'Show_Mouse' somewhere in your program for every Hide_Mouse you execute. If you don't, when your program is finished, GEM will still be hiding the mouse when you get back to the Desktop ... not a pretty 'sight'! OK. The mouse is in hiding, and I can safely OPEN a new window, using the procedure 'Open_Window' (of course). I tell GEM to open the window it has reserved and to give me the maximum amount of space to work with (the 0,0,0,0 tells GEM to make the window as BIG as possible, which generally means full-screen. Don't exceed the values that you used in the New_Window function call...). To make SURE that the window I just opened is the one I will be using, I use the procedure 'Set_Window'. If you have several windows open at once, you use Set_Window to tell GEM which one you're going to be working with now. Notice that the variable 'my_window' lets GEM know WHICH window I'm talking about. The window is open and set. I want to find out how much space I have to work with, so I use the procedure 'Work_Rect'. The variables x,y,w, and h are given values by the procedure Work_Rect, and I can use them in my program to determine several things. The x and y tell me where the 'origin' of the window is, and w and h tell me how wide and high the window is. This is useful information -- it means I don't have to write separate procedures for each resolution mode (LOW, MEDIUM and HIGH resolution). Instead, I can restrict myself to the boundaries given by these values. I can do better than that. I can tell GEM to ignore things I draw that are outside the window boundaries by making a call to 'Set_Clip'. This procedure will take the values I got from Work_Rect and tell GEM not to do any actual drawing outside of those boundaries. Now, regardless of how big or small my window is, I can draw on it without fear of crashing the system by going outside of the window boundaries. I have my window on screen, but it's got the background color of the GEM Desktop. I much prefer to draw on a white background, so I set the 'Paint_Color' to white and then tell GEM to paint the entire window that color using 'Paint_Rect'. Notice that the width and height are passed to Paint_Rect. I have set the origin at 0,0, which is the upper left corner of the window (or it's supposed to be ... try it using x,y instead of 0,0 and note the results). OK, I have a white background, so I'm going to draw things with black lines. I set 'Line_Color' and 'Text_Color' to black. Then I call procedure 'Draw_String'. The two parameters are x and y pixel values, x going horizontally and y going vertically. 10,10 is 10 pixels to the right and 10 pixels down. Next, I draw some lines. Ideally, I would be drawing a square, with corners at 15,15 and 100,100. However, it may not look like it on your screen. The reason for this is that there are MORE x pixels than there are y pixels on your screen. In LOW resolution there are 320 x 200 pixels. In HIGH resolution there are 640 x 400 pixels. In MEDIUM resolution there are 640 x 200 pixels. The first number is the x pixel value, the second number is the y pixel value. For now, we'll not worry about it. BUT, if you're going to be doing lots of graphics, you'll want to set up some sort of "fudge-factor" to make things look right. One possible solution is to multiply the x value by some constant based on the resolution mode you are in. For example, in MEDIUM resolution, multiplying x by 3.2 would compensate. (3.2 x 200 = 640.) There are two procedures for drawing lines: 'Line' and 'Line_To'. The first requires two endpoints, the beginning of the line and the end of the line. The second requires one endpoint, using the end of the last line as the beginning of the new line. My lines are drawn, and all of the text is also drawn. The second Draw_String is an absolute MUST. The reason? You should ALWAYS let the person using your program know what to do next! If the second line were omitted, I would have no idea that I was supposed to press the left mouse button to exit the program. It is good programming practice to include such 'hints' where they are needed. How do I tell when the left button is pressed? I use the Pascal function 'Get_Event'. There are a LOT of parameters for this function. Some are not needed just to test for a simple click on the left mouse button. These are duly noted in the VAR declaration part of the program. 'my_event' is an integer value returned by Get_Event. In many cases, your program would take this value and continue to do something else. Here, it simply sets up the program to await a click of the 'ol mouse button. Get_Event parameters needed to test the mouse button include E_Button ("I'm waiting for a mouse button event..."), and several values that follow. The first '1' means "left mouse button", the second '1' means "waiting for user to click button", the third '1' means "wait for one click" and the '0' means "wait this long for the button click". In this case, that '0' is meaningless (as are the 'False' and other '0's in the parameter list) since we are not using the E_Timer event flag. Everything else is simply there for completeness. Once I click the mouse, I close the window I created with 'Close_Window'. Then I 'Show_Mouse', because I hid it earlier and I want to SEE the mouse arrow once my program is finished! Then I 'Delete_Window', which releases the space GEM has allocated for my window since I no longer need it. Done at last. Look at all of that work, simply to draw a couple of lines on the screen! However, the next time I want to create a window and draw some lines, it'll be that much easier... Of RAM disks and Pascal programming If you have a 1040 ST, or a 520 ST with a 1 megabyte memory upgrade, you can move the OSS Personal Pascal files from disk to a RAM disk. This makes things like calling the compiler and linker MUCH faster. You need to make sure that the RAM disk you are using can survive a computer RESET. Talk to your local Atari ST user group to see if they have a copy of one of the many RAM disks available. One such is called ETERNAL.PRG, another is available in an archive file called YARD.ARC. To automate moving your Pascal program files to the RAM disk, you need to look for a program such as FLDR2DSK.PRG, also available from many bulletin boards and user groups. I use this program, but do not put it into the AUTO folder as the instructions mention. The reason is that my RAM disk is created on a number of disks, and there is no real reason to reboot my system simply to load OSS Personal Pascal. If you own an Atari 520 ST without the memory upgrade, you can speed things up somewhat by using a RAM disk and putting your programs on the RAM disk instead of Pascal. There are some instructions available to do this, which should be available from your local user group. If you can't find them, I will be happy to copy them for the cost of a STAMPED self- addressed envelope. About the best you can hope for is a 200k RAM disk. Wrapping things up I'd like to thank everybody who sent me letters and E-Mail regarding the first column. In future months, you can expect to see some more articles on working with OSS Personal Pascal. In next month's article, I will discuss reading from and saving to files. I'll also talk about using the pre-defined DIALOG boxes that are accessible from OSS Personal Pascal. I'm open to answering some SIMPLE questions regarding Personal Pascal. You can reach me via GEnie as D.MEILE, or write (include a stamp, please) to: David Meile Box 13038 - Dinkytown Station Minneapolis, MN 55414 Additional reading If you are interested in a college text for Pascal, you might find that AN INTRODUCTION TO PROGRAMMING AND PROBLEM SOLVING WITH PASCAL by G. Michael Schneider, Steven W. Weingart and David M. Perlman is useful. It is published by John Wiley and Sons, and the latest version was published in (I believe) 1984. It was the text used for beginning programming classes at the University of Minnesota in Minneapolis when I learned to program in Pascal.