{ ----------------------------------------------------------------------------- NOTICE: THESE MATERIALS are UNSUPPORTED by OSS! If you do not understand how to use them do not contact OSS for help! We will not teach you how to program in Pascal. If you find an error in these materials, feel free to SEND US A LETTER explaining the error, and how to fix it. THE BOTTOM LINE: Use it, enjoy it, but you are on your own when using these materials! DISCLAIMER: OSS makes no representations or warranties with respect to the contents hereof and specifically disclaim all warranties of merchantability or fitness for any particular purpose. This document is subject to change without notice. OSS provides these materials for use with Personal Pascal. Use them in any way you wish. -------------------------------------------------------------------------- } MAKING GENERIC AES AND VDI CALLS FROM PERSONAL PASCAL ----------------------------------------------------- As some of you have discovered, because of prior experience with the Atari ST developer's package, there are a number of calls within the GEM system (AES and VDI) which are not yet supported by Personal Pascal. Luckily, within the PASGEM library there are two routines which already support additional calls to GEM. This document gives you the information you need in order to call these two generic GEM routines. AES CALLS --------- First of all, we are going to tackle AES calls. In the process of explaining how to call the generalized AES routine, we will be implementing a new call which is not supported by Pascal. This routine, which is called "graf_mkstate" in the C bindings, returns the current state of the mouse buttons and the key- board modifier keys (i.e., alternate, left and right shift keys, and control). This routine takes four parameters which are the addresses of four two-byte variables in which to put the mouse and keyboard state information. Since passing an address in Pascal is equivalent to passing a variable as a VAR parameter, the declaration of the routine we're going to construct is going to start like this: { Mouse_Key_State - Return the current mouse position and the state of both the mouse buttons and the keyboard modifier keys. } PROCEDURE Mouse_Key_State( VAR x, y, buttons, keys : integer ) ; Before we start filling in the rest of the procedure, we have to look at how parameters are passed to the AES. There are four separate areas in which values are passed to and returned from AES. The first area is the "global parameter area", where AES stores various parameters it needs to keep around between calls. Since the application program should not modify these values, there is no way to access the "global" array from Pascal. The second area is the "integer input array", in which various integer values may be passed to AES. Similarly, there is an "integer output array" in which AES passes values back to the calling program. The fourth and fifth arrays are the "address input array" and the "address output array". These two areas will contain address parameters passed to or from AES. The Pascal library keeps track of the global parameter area, since it must remain intact, but the other arrays must be declared in your GEM program if you want to make calls to the AES handler. In order to declare the arrays easily, we will set up their types first: TYPE Pointer = ^char ; { Just a filler declaration! } Int_In_Parms = ARRAY [ 0..15 ] OF integer ; Int_Out_Parms = ARRAY [ 0..45 ] OF integer ; Addr_In_Parms = ARRAY [ 0..1 ] OF Pointer ; Addr_Out_Parms = ARRAY [ 0..0 ] OF Pointer ; The declaration of "Pointer" is just used to emphasize that the address in and out parameters are ADDRESSES, and not just numeric values. Notice that the integer arrays only have lengths 16 and 46, respectively. This is sufficient for most calls, but if you want to make a call to VDI (see below) which needs more slots in these arrays, increase the size accordingly. Now that we know the TYPEs of the local variables we need, we can declare them: VAR int_in : Int_In_Parms ; int_out : Int_Out_Parms ; addr_in : Addr_In_Parms ; addr_out : Addr_Out_Parms ; OK, we're ready to look into the actual routine which we will be calling to interface to GEM. It takes five parameters. The first is the AES call number, which is 79 for out "graf_mkstate" call. The next four parameters are just the arrays which we just declared, passed as VAR parameters. The routine should be declared EXTERNAL as follows: PROCEDURE AES_Call( op : integer ; VAR int_in : Int_In_Parms ; VAR int_out : Int_Out_Parms ; VAR addr_in : Addr_In_Parms ; VAR addr_out : Addr_Out_Parms ) ; EXTERNAL ; Now that we know all of our variables and parameters, and everything the AES_Call routine is expecting, we can look at what we need to do to actually perform the GEM call. According to the AES documentation, the "graf_mkstate" call doesn't expect any parameters, and it returns the results in the "integer output array" as follows: int_out[0] -- error code (0 if no error occurred) int_out[1] -- current mouse x position int_out[2] -- mouse y position int_out[3] -- mouse button state int_out[4] -- keyboard modifier state We should never get an error with this call, since no parameters are passed in, so we're going to ignore the error code. This isn't a good idea in general, but it simplifies our presentation somewhat. The complete code required to perform the call and return the result values in the proper parameters is as follows: BEGIN AES_Call( 79, int_in, int_out, addr_in, addr_out ) ; x := int_out[1] ; y := int_out[2] ; buttons := int_out[3] ; keys := int_out[4] ; END ; To summarize this section on making AES calls, here is a complete listing of the Mouse_Key_State routine, without the intervening text: { Mouse_Key_State - Return the current mouse position and the state of both the mouse buttons and the keyboard modifier keys. } PROCEDURE Mouse_Key_State( VAR x, y, buttons, keys : integer ) ; TYPE Pointer = ^char ; { Just a filler declaration! } Int_In_Parms = ARRAY [ 0..15 ] OF integer ; Int_Out_Parms = ARRAY [ 0..45 ] OF integer ; Addr_In_Parms = ARRAY [ 0..1 ] OF Pointer ; Addr_Out_Parms = ARRAY [ 0..0 ] OF Pointer ; VAR int_in : Int_In_Parms ; int_out : Int_Out_Parms ; addr_in : Addr_In_Parms ; addr_out : Addr_Out_Parms ; PROCEDURE AES_Call( op : integer ; VAR int_in : Int_In_Parms ; VAR int_out : Int_Out_Parms ; VAR addr_in : Addr_In_Parms ; VAR addr_out : Addr_Out_Parms ) ; EXTERNAL ; BEGIN AES_Call( 79, int_in, int_out, addr_in, addr_out ) ; x := int_out[1] ; y := int_out[2] ; buttons := int_out[3] ; keys := int_out[4] ; END ; VDI CALLS --------- Accessing the VDI system is very similar to the discussion of AES calls above. The only main difference is that, although there is a "global parameter array", it doesn't need to stay intact. Also, sometimes you need to get return values in this array. Also, no address parameters are ever passed, but a new type of value is passed and returned, points. So the TYPE declarations for the various arrays we need are slightly different: TYPE Ctrl_Parms = ARRAY [ 0..11 ] OF integer ; Int_In_Parms = ARRAY [ 0..15 ] OF integer ; Int_Out_Parms = ARRAY [ 0..45 ] OF integer ; Pts_In_Parms = ARRAY [ 0..11 ] OF integer ; Pts_Out_Parms = ARRAY [ 0..11 ] OF integer ; For our VDI calling example, we're going to implement the call which allows you to control the height of text that is drawn using the "Draw_String" call. This call is known in the VDI documentation as "vst_height", but we're going to declare it like this: { Text_Height - Set the height in pixels of text, when it is drawn using the Draw_String library call. } PROCEDURE Text_Height( height : integer ) ; Again, we need to declare the variables which we are going to pass to VDI: VAR control : Ctrl_Parms ; int_in : Int_In_Parms ; int_out : Int_Out_Parms ; pts_in : Pts_In_Parms ; pts_out : Pts_Out_Parms ; The actual generic routine we are going to call to perform VDI operations is very similar to the AES_Call routine described above. One difference is that we pass two command numbers instead of one. The second number is only used when we call the GSX graphics primitives; it is the GSX primitive number which we want to use. For all non-GSX calls (i.e., most of the time), this second number will be zero (as it is in this case). Also, there is one additional parameter, called "translate" in the declaration below, which specifies whether to translate the points in the "pts_in" and "pts_out" array RELATIVE to the current origin. What this means is that if you use the Set_Window call to make a window current, all points passed to or from VDI will be translated to screen coordinates such that (0,0) is equivalent to the upper left of that window, PROVIDED the value of "translate" is true. If you don't want such translation to occur (we don't, in this case, since the "point" we are passing is actually the height we want!), pass in "false" for this parameter. The declaration of the generic VDI call is as follows: PROCEDURE VDI_Call( cmd, sub_cmd : integer ; nints, npts : integer ; VAR ctrl : Ctrl_Parms ; VAR int_in : Int_In_Parms ; VAR int_out : Int_Out_Parms ; VAR pts_in : Pts_In_Parms ; VAR pts_out : Pts_Out_Parms ; translate : boolean ) ; EXTERNAL ; Notice that we must tell VDI the number of integers and points which we are passing. The particular call we want to use is number 12, "set character height, absolute mode". It expects two parameters, as follows: pts_in[0] -- 0 (the value zero) pts_in[1] -- desired height in pixels It returns several parameters: pts_out[0] -- character width selected pts_out[1] -- character height selected pts_out[2] -- character cell width selected pts_out[3] -- character cell height selected Why are there four return values instead of two? The first two (0 and 1) are usually SMALLER than the other two, for the following reason. The "character height" (not cell height) is measured from the baseline (the bottom of capital letters) to the top line (the top of capitals, including a little space). The character width is, similarly, measured from the left edge to the right edge of characters. The "cell width" and "cell height", on the other hand, are measured from the very bottom to the very top and the very left to the very right of the "cell" in which a character is drawn. Since some space is put on all sides of a character, the "cell" measurements are a little larger than the other measurements. We're actually going to ignore all of the return parameters, since we just want to set the values and assume they are correct. The main body of our PROCEDURE is, then: BEGIN pts_in[0] := 0 ; pts_in[1] := height ; VDI_Call( 12, 0, 0, 2, control, int_in, int_out, pts_in, pts_out, false ) ; END ; In order to look at the routine as a whole, here are all the neccessary declarations and code together, without the intervening text: { Text_Height - Set the height in pixels of text, when it is drawn using the Draw_String library call. } PROCEDURE Text_Height( height : integer ) ; TYPE Ctrl_Parms = ARRAY [ 0..11 ] OF integer ; Int_In_Parms = ARRAY [ 0..15 ] OF integer ; Int_Out_Parms = ARRAY [ 0..45 ] OF integer ; Pts_In_Parms = ARRAY [ 0..11 ] OF integer ; Pts_Out_Parms = ARRAY [ 0..11 ] OF integer ; VAR control : Ctrl_Parms ; int_in : Int_In_Parms ; int_out : Int_Out_Parms ; pts_in : Pts_In_Parms ; pts_out : Pts_Out_Parms ; PROCEDURE VDI_Call( cmd, sub_cmd : integer ; nints, npts : integer ; VAR ctrl : Ctrl_Parms ; VAR int_in : Int_In_Parms ; VAR int_out : Int_Out_Parms ; VAR pts_in : Pts_In_Parms ; VAR pts_out : Pts_Out_Parms ; translate : boolean ) ; EXTERNAL ; BEGIN pts_in[0] := 0 ; pts_in[1] := height ; VDI_Call(12, 0, 0, 2, control, int_in, int_out, pts_in, pts_out, false); END ;