Topic : TOS - das Betriebssystem Author : Version : tos.hyp (5. März 2013) Subject : Programmieren/Atari Nodes : 3001 Index Size : 93602 HCP-Version : 5 Compiled on : Atari @charset : atarist @lang : @default : Titel @help : @options : +g -i -s +x +zz -t4 @width : 70 View Ref-File15.10.10 How to implement an SRA TOS Here's a step-by-step instruction on how to implement SSP as a Service Requesting application. All code provided here is also available as sample code in the archive in the file SRAtools.c (SRAtools.gfa for GFABasic, SRAtools.s for assembler). Use and modify as necessary :) The data identification An important thing is that the SRA does not really request or distinguish between specific services. The SRA rather determines a dataID depending on the user action, and the type of object the user action leading to the use of system services was performed on, and sends this dataID to the Server. The Server will then figure out which services can be used with the provided data. This makes it possible to implement new services into the SSP-Server using the same data-types without changing the SRAs. Communication Session A second concept that should be understood is the use of sessions. A session between an SRA and the Server is started with the SSP_SRASR message, and ended when the SRA receives SSP_SSA. The Server can handle multiple sessions for one SRA and multiple SRAs at a time (up to 32 sessions per SRA at a time, and 32 different SRAs at a time, that's 1024 parallel SRA sessions, meaning the use of 1024 service requests at a time, max). Every session is usually bound to a certain shared memory block that contains the data necessary for the session. One SRA session always refers to one service request. For this, it is necessary also that the SRA can distinguish between different sessions. Every session has an ID number, which also makes up the last characters of the shared memory filename (shmID passed in SSP_SRASR). Multiple sessions from your application at a time can occur for example like this: ∙ AtarIRC as SRA of Status Display Service sends SSP_SRASR with dataID SSP_STATUSICON to change the status display, due to starting a dcc download from a user ∙ Before the Server acknowledges with SSP_SSA, the user starts a dcc send to another user; a second SSP_SRASR message is sent, opening a second session and hence providing a second shared memory block and the corresponding file in u:/shm/ To implement this, a simple 32-bit bit-vector can be used to determine if a session is active or not. Each bit represents a possible session. Before starting a session (before sending SSP_SRASR), check the bits from 0 to 31. If a bit is not set, another session can be opened. Set the bit and use the index of the bit as the session ID (shmID). This code uses the global 32-bit integer sessionVector as the indicator for active sessions, and returns the next inactive session number, or -1, if all possible 32 sessions are active: long sessionVector; int determineSessionID(void){ int idCounter, noMoreSessions=1; for(idCounter=0; idCounter if( (sessionVector&(1<<idCounter)==0 ){ sessionVector |= (1<<idCounter); noMoreSessions = 0; break; } } if(noMoreSessions) return(-1); else return (idCounter); } 1. Finding the Server The SRA has to find the Server's appl_id in order to send messages. Do this at program startup and store the application id of the Server for later use. This is done basically the same way it is in OLGA: ∙ Read environment variable SSP_SERVERNAME to retrieve the Server application name ∙ Use appl_find to determine the Server's appl_id ∙ If the Server can't be found: - Read environment variable SSP_SERVERPATH to retrieve the Server application's path and filename - Start Server with shel_write - Read environment variable SSP_SERVERNAME to retrieve the Server application name - Use appl_find to determine the Server's appl_id - If the Server application is not in the specified path or if starting fails: * output an understandable error message to the user, possibly referring to a chapter in your application's documentation * continue starting your application, not using SSP SSP is an addition to the system, unavailability should not interfer with your application's usual behaviour, except your application explicitly requires SSP to work (complete applications of different kinds that basically consist of an SSP SRA client are possible). 2. Using a service 2.1 Determining the data-type To use a service, the SSP_SRASR AES message has to be sent to the Server. Before sending the message, determine the data-type used. For example, if a block has been selected in an editor and the user wants to use SSP by right clicking and choosing System Services from the context menu, or from the editor's menu bar, the data-type (referred to as dataID) is SSP_TEXT. If for example an image file or MP3 is to be handled via SSP, the data-type should be SSP_FILENAME. For the Status Display Service, the data-type has to be SSP_STATUSICON. This service should be initialized automatically, without user interaction, after the Server has been started, at program startup. See also 'Data identification' for SSP_SRASR for short explanations of the data-types and the corresponding data in the shared memory block. 2.2 Requesting a service 2.2.1 Creating shared memory file Data is sent in a shared memory file. Whether one has to be created or not depends on the dataID (thus on the service type). There is a unique ID for every shm-file used for each service request, and the name of the file is built by the following conventions: u:/shm/[appl_id]_data[shmID].ssp For example if Smurf with appl_id 25 is sending a file and chose ID 0: u:/shm/25_data0.ssp The shared memory ID is vital to the Server, because it is used to distinguish between different service requests and SRA communication sessions that are 'alive' at the same time. A session is 'alive' between the sending of SSP_SRASR and receiving SSP_SSA. For example, the user could make your application request Send File Service, while you haven't gotten the SSP_SSA response for the SSP_STATUSICON request. This would make you open a second session, and a second shared memory file, while the session for the status display is still alive. There are two different ways to handle this: 1. Wait until the SSP_SSA response for one request has come in before sending the second request 2. Keep track of sent requests and received responses. Make an array, the index being the number of the request sent (which can be the shmID as well), and set the array value to 1 if a request has been sent. If SSP_SSA comes in, set the referring array value to 0. Use only indices (shmIDs) if the array value is 0. The second way is recommended. After you have built the name for the shared memory file, use Fcreate to create the file. Read/Write access rights have to be set correctly, depending on the request type! Then, bind the memory block that contains the data (text, filename, icon data, etc., depending on dataID) to the file using Fcntl. Close the file with Fclose. The following code calls the above routine detemineSessionID, uses the returned ID to build the filename and creates the file. The function openSession takes two parameters: access describes the access rights to the memory block, and *memPtr is a pointer to the memory block to share. buildFilename takes a determined session ID as parameter, builds a filename from it and returns a pointer to the static string containing the filename. buildFileName assumes appl_id being the SRA's (your) application ID available as a global variable! int openSession(int access, void *memPtr) { int fHandle; int sessionID = determineSessionID(); char *filename = buildFileName(sessionID); fHandle = Fcreate(filename, access); Fcntl(fHandle, memPtr, SHMSETBLK); return(sessionID); } char *buildFilename(int sessionID) { static char filename[256] = "u:/shm/"; char tmpstring[4]; strcat(&filename, itoa(appl_id, tmpstring, 10)); strcat(&filename, "_data"); strcat(&filename, itoa(sessionID, tmpstring, 10)); strcat(&filename, ".ssp"); return(&filename); } The return value of openSession is the new sessionID which can be used to pass to the Server in the service request. The filename could also be stored, but that's not really necessary because the Server passes the shmID that it received from the SRA back to it, in the SSP_SSA acknowledge message, so the filename can be built again to delete the file. 2.2.2 Sending a Service Request Send an SSP_SRASR message to the Server, filling the AES message- buffer with the following values: [0] = SSP_SRASR [1] = appl_id [2][3] = (long)dataLen [4] = dataID [5] = shmID appl_id (messagebuf[1]) This is your appl_id. Make sure to fill this correctly, because the Server will distinguish different SRA-communication sessions by the application IDs of the SRAs. dataLen (messagebuf[2]/[3]) The length of the data to transfer (32 bit!). For example the length of the string to send if dataID is SSP_TEXT or SSP_FILENAME, or the length of the icon data in the SSP_STATUSICON case. messagebuf[2] holds the upper 16 bits, [3] the lower 16 bits: messagebuf[2] = (int)(dataLen>>16); messagebuf[3] = (int)(dataLen&0xFFFF); dataID (messagebuf[3]) The identification value of the data you determined, for the Server to find out which services can be possibly used. shmID (messagebuf[4]) The identification number for the shared memory block (your session ID). This is absolutely necessary for the Server to distinguish different sessions with one SRA. If the Server is in debug mode (environment variable SSP_SERVERDEBUG is defined), it will print out all information received from the SRA in an understandable form into a window, and write it to a logfile sspdebug.log in the same directory the Server application is in. It will post error-messages and possible suggestions on how to fix the error in both the window and the log file. 3. Closing a session on SSP_SSA When your application receives SSP_SSA, this means that a service has been performed (DRS), or has been started to be performed (IRS). messagebuf[1] will contain the shmID you passed to the Server in SSP_SRASR. This makes it possible for you to find out which session the response is for. Build a filename like in 2.2.1 (or store the filename when sending SSP_SRASR) and delete the shared memory file, free all temporary memory you have possibly allocated for the service request, and you're done. The following code uses the above function buildFilename and the sessionID from messagebuf[1] as a parameter to build a filename, delete the shm-file, and clear the bit of the session in the global sessionVector variable: int closeSession(int sessionID){ char *fileName = buildFilename(sessionID); return Fdelete(fileName); sessionVector &= ~(1-sessionID); } Don't forget to free temporary memory that you might have allocated to compose the data. You could also store the memory pointer for each session in an array of pointers and implement the Mfree/free into the closeSession function. NULL pointers in the array could indicate that no temporary buffer was allocated (e.g. when you share your internal buffers for filenames, text or other data, directly). This could be done by adding a parameter tmpFlag to the openSession function that indicates if memPtr is a temporary buffer. openSession could then automatically insert memPtr or NULL at the appropriate position in a global array, one entry for every session, with sessionID as the index. closeSession could then get the pointer from the array at the index of its parameter sessionID and perform @{"Mfree" ignore} on the pointer, if it's not NULL. This would automate and connect the processes of opening and closing sessions internally, creating and deleting shared memory files, and freeing possible temporary buffers. 4. The different dataIDs and their impact on sessions 4.1 SSP_STATUSICON request and the Status Display Service The dataIDs have an influence on how a session and service is handled internally. For SSP_TEXT and SSP_FILENAME, you don't have to worry about it, just go by the above scheme. For SSP_STATUSICON however, there is a twist. The status icon will be displayed by all applications that are switched on for Status Display Service. The service is invisible, meaning it won't be triggered by a user action rather than automatically, on program startup and anytime the status of your program changes. When that happens, and you want to display a different status icon, you have to send another request with SSP_STATUSICON as dataID. All 'on' applications for Status Display Service will then update the icon they display for your application's status. When you exit your application and have sent a service request with SSP_STATUSICON, you have to inform the Server that your status icon is to be removed from all displaying applications! Implement a service request with the dataID SSP_ENDSDS into the exit code of your application. The Server will then inform all applications that display your status icon that the icon has to be removed. A nice idea would be to display the download status of internet applications in the status icon, by making the icon a 16 pixel high or wide progress bar. Update the status icon every time enough data has been downloaded to fill another pixel line of the progress bar, and there you go. Memory could be saved, if necessary, by updating the icon data dynamically instead of including a separate icon for every progress bar phase. 4.2 Requesting SSP_STATUSICON will make your SRA an SPA Applications that provide Status Display Service will usually be able to request the SSP_DISPLAYINFO and SSP_CONTEXTPOPUP services. This means that if you request service with SSP_STATUSICON as dataID, you will probably receive a request for SSP_DISPLAYINFO or SSP_CONTEXTPOPUP at some point! Look at How to implement an SPA on how to provide the Display Information and Context Popup services. 4.3 The icon data The icon data has to be put into the shm-block as follows: mono_icon1 monomask1 [16col_icon1 16colmask1] [256col_icon1 256colmask1] [CRLF] [mono_icon2 monomask2] [16col_icon2 16colmask2] [256col_icon2 256colmask2] NULL Entries in [] are optional. That means the SRA has to send at least one monochrome status icon and its mask. The application providing the status display will pick the icon best for the current screen colour depth. The mask data directly follows the icon data without any separation! The spaces above are just for readability. All mask data is 1 bit, as provided by the GEM ICNBLK and CICNBLK structures. The xx_icon2 data blocks are optional second icons for animation. The SPA will read, if provided, both icon data blocks for the current screen mode and switch in 1 second intervals between the 2 icon blocks. This is useful to make status changes noticeable. To make the 'blinking' animation stop, send another service request with SSP_STATUSICON and only the xx_icon1 icon data. Blinking should be done for no longer than 8 seconds, after that the second status display request should be performed by your application to switch to a static icon. The data should be in the regular screen format, meaning you should incorporate possible status icons in your application's resource file, and copy the raw graphics data from the icon objects into the memory block that will be shared, one after another, and terminate with a NULL byte. The icons must have 16x16 pixel dimensions. A good approach would be to have the status icons in every available colour depth in the above format ready in each a memory block, to be able to request the service without having to copy around icon data. That makes the interactions between SRA and SPA quicker and saves the hassle of building memory blocks before every request. Memory saving is not really an issue here, as one icon in all 3 colour depths uses just 512 bytes, so even keeping 20 different icons in all 3 colour depths, and their 2-phase animated pendants, in memory, would only cost 30 kBytes. This can be reduced to 20 kBytes by just keeping the 2-phase versions in memory and inserting NULL or CRLF at the middle position to switch between animated and static versions. Usually it will be less, because 256- colour icons will rarely be needed at 16x16 pixels size. And, which application really has 20 different statii? 5. Guidelines for implementing SPAs ∙ Make your icons for the Status Display Service distinguishable (so people can see it is this specific application's status icon) and recognizeable (so the user doesn't have to wonder 'what the heck is that supposed to be?'). ∙ Use status icons intelligent and carefully. There is no point in displaying a different status for an operation that only takes 1/10 second. ∙ The SSP protocol is rather easy to implement on SRA side. Try to make the implementation as transparent to the user as possible, without the user noticing much of the different applications and processes involved. ∙ Request invisible services like Status Display really invisible, without any direct output to or input from the user. ∙ After implementing SSP, switch the server into debug mode and try requesting different services with different data types. If everything works and the server outputs no error or warning messages, your implementation is OK. ∙ Remember, the user doesn't care much about how it works, only that it works.