The AfterStep Module Interface by Hannu Rummukainen (hrummuka@cc.hut.fi) ************************************************************************ This document is intended to serve as an introduction to the AfterStep module interface and its capabilities. This is not meant to be a comprehensive reference guide; make anything involved and you are bound to be reading AfterStep sources anyway. Some background knowledge on xlib and unix programming is assumed. A module is basically a separate unix program that AfterStep starts and communicates with. A module can be started during AfterStep initialization via the Module option, or at any time during the X session by the Module built-in. A module can run indefinitely until AfterStep exits, or can perform a single task and exit. Module Invocation ================= A module should be an executable file located in one of the directories listed in the .steprc ModulePath option. It is referenced in AfterStep commands by the name of the executable. AfterStep gives a module 5 or 6 command line arguments (in addition to its path name in argv[0]), as follows: argv[0] The path and name used by AfterStep to start the module. argv[1] The file descriptor for the open pipe that the module must write to in order to send commands and requests to AfterStep. This is an integer formatted in decimal. argv[2] The file descriptor for the open pipe that the module must read to in order to acquire any information sent by AfterStep. This is an integer formatted in decimal. argv[3] Base file (paths) usually ~/GNUstep/Library/AfterStep/base argv[4] The Window ID of the application window that was focused when the module was invoked. The argument is zero if no window was focused, or if the Module was started during AfterStep initialization. argv[5] The AfterStep context of the module invocation. Briefly, when the previous argument specifies a window, this argument tells which part of the window (title bar, client area etc) was concerned. The AfterStep contexts are defined in the include file "appropriate path/afterstep/afterstep.h". This is an unsigned 32 bit integer formatted in hexadecimal. argv[6] (optional, check argc to be sure) Any command line arguments for the module, if such were supplied in the Module command that invoked this module. They are all sent as a single null-terminated string. The DISPLAY environment variable points to the display managed by the AfterStep instance that started the module. Sending commands to AfterStep ============================= The data sent to AfterStep consists of packets with a structure like this: Window w; Window ID for the window to be affected, or None. The window ID can refer to an application window or to a decoration window made by AfterStep, including the top-level frame window. int size; The length of the text string to follow in characters. The size is limited to no more than 255 characters. char text[size]; A text command formatted similarly to the function field in the mouse and key bindings in .steprc. See below for special commands available in Modules. int cont; If this is nonzero, everything goes on as usual. On the other hand, if the integer value is zero, AfterStep closes the pipes to the module permanently. The text commands are rather thoroughly documented on the AfterStep manual page. However, there are a couple of additional commands that can only be issued by a module: Set_Mask maskvalue Set the input mask for the module, in order to specify what actions AfterStep should inform the module about. The input mask values are defined in the include file "appropriate path/afterstep/module.h". Send_WindowList Get the AfterStep window list in its entirety. See below for more information. Unlock Let AfterStep return to normal after it's been locked. When AfterStep is about to exit, it closes down the communication pipes. Modules should detect this and respond with SIGCHLD when they are ready to let go. However, in 30 seconds AfterStep exits anyway. The locking facility is a kludge to allow animating of iconifying windows. A better alternative is on the way, and I do not recommend depending on the current implementation. For now, when you set the input mask bit M_LOCKONSEND, AfterStep stops on its tracks when sending any packet to your module. You have to acknowledge each and every packet by a Unlock command. Reading information from AfterStep ================================== AfterStep sends information to modules in small packets. By default a module will get all types of packets, but it is recommended that you select (with the Set_Mask command) exactly the types of packets that you need. Every packet sent to modules begins with a header: unsigned long START_FLAG; A special packet start flag defined in module.h unsigned long packet_type; A packet type identifier, equivalent to the input masks defined in module.h unsigned long packet_length; The total length of the packet in unsigned longs, including everything from the start flag to the last unsigned long. The contents of the rest of the packet depend on the packet type. Each type contains a constant amount of data items, as shown by the table below. Of course, a future version might send more data so using the packet length field is a good idea. Every data item is cast to unsigned long before sending, except for strings which are merely copied into place. Of the common data items below: `app_win' is the window id of the real client application window, `frame' is the window id of the top-level frame window, which is an ancestor of the application window, and a child of the root window. The corresponding ASWindow structure pointer is there as well, what it is good for I do not know. ------------------------------------------------------------------------------ packet type #data what the data items are for, in order and with items types to cast them back to, if necessary a brief explanation ------------------------------------------------------------------------------ M_ADD_WINDOW 0 - A new window is being created. Followed by M_WINDOW_NAME, M_ICON_NAME, M_RES_CLASS and M_RES_NAME. M_DESTROY_WINDOW 3 Window app_win, Window frame, ASWindow* t A window is destroyed. M_FOCUS_CHANGE 3 Window app_win, Window frame, ASWindow* t or: 0, 0, 0 Contains the identity of the window that just got the focus. If the window is not managed by AfterStep, all the data values are sent as zero. M_MAP 3 Window app_win, Window frame, ASWindow* t A window is mapped. This is not sent if the mapping is due to deiconification. M_RAISE_WINDOW 3 Window app_win, Window frame, ASWindow* t A window is raised. Note: these messages are not a reliable way of tracking the window stacking order; should you want to do this, do an occasional Send_WindowList or communicate directly with the X server. M_LOWER_WINDOW 3 Window app_win, Window frame, ASWindow* t A window is lowered. M_ICONIFY 7 Window app_win, Window frame, ASWindow* t, icon_x, icon_y, icon_width, icon_height A window is iconified. When this packet is sent as part of a window list, the four last data items are zero in case the window is iconified but the icon is unmapped. M_DEICONIFY 3 Window app_win, Window frame, ASWindow* t, icon_x, icon_y, icon_width, icon_height A window is deiconified. The last four data items are zero if the deiconification was initiated by the application itself. M_ICON_LOCATION 7 Window app_win, Window frame, ASWindow* t, new_icon_x, new_icon_y, icon_width, icon_height An icon was moved. M_TOGGLE_PAGING 1 0 or 1 The TogglePage built-in is called; the data value is 1 if the edge scrolling is enabled and 0 if it is disabled. M_NEW_PAGE 3 new_x, new_y, desknumber The virtual screen is scrolled. M_NEW_DESK 1 new_desknumber A change of desktop. M_SHADE 3 Window app_win, Window frame, ASWindow* t A window is shaded. M_UNSHADE 3 Window app_win, Window frame, ASWindow* t A window is unshaded. M_NEW_BACKGROUND 1 0 or 1 User decided to use another pixmap for background and pager must update its pixmaps. M_END_WINDOWLIST 0 An end marker for the Send_WindowList packetfest. See below. M_CONFIGURE_WINDOW 24 Window app_win, Window frame, ASWindow* t, frame_x, frame_y, frame_width, frame_height, Desk, flags, title_height, boundary_width, hints.base_width, hints.base_height, hints.width_inc, hints.height_inc, hints.min_width, hints.min_height, hints.max_width, hints.max_height, 0, icon_pixmap_w, hints.win_gravity, TextPixel, BlackPixel Some properties or flags associated with a window were changed. Except for the zero and the ASWindow pointer, the data items in the packet are fields of the ASWindow structure (see afterstep.h). M_WINDOW_NAME 3+s Window app_win, Window frame, ASWindow* t, char[] window_name The title of a window is set or changed. M_ICON_NAME 3+s Window app_win, Window frame, ASWindow* t, char[] icon_name The icon name of a window is set or changed. M_RES_CLASS 3+s Window app_win, Window frame, ASWindow* t, char[] res_class The class name of a window is set. M_RES_NAME 3+s Window app_win, Window frame, ASWindow* t, char[] res_name The application name of a window is set. ------------------------------------------------------------------------------ There is no corresponding packet for the mask value M_LOCKONSEND. In response to a Send_WindowList request, AfterStep sends the following packets to the module that sent the request: M_TOGGLE_PAGING M_NEW_DESK M_NEW_PAGE * for each window: M_CONFIGURE_WINDOW M_WINDOW_NAME M_ICON_NAME M_RES_CLASS M_RES_NAME * if the window is iconified, M_ICONIFY (see the M_ICONIFY description for a special note on this) * to terminate the window list: M_END_WINDOWLIST The AfterStep library ===================== There is a library of utility routines available in the lib directory of the source distribution. Your module can take advantage of the library by including the include file aftersteplib.h and linking to libafterstep.a. Here's a lowdown of the routines in the library: void SendText(int *fd,char *message,unsigned long window); void SendInfo(int *fd, char *message, unsigned long window); Send a command to AfterStep. The fd argument is the file descriptor of the module-to-AfterStep pipe, message is the text command to be sent, and window is None or the window ID of the window to be affected. The continuation flag in the packet is set. Interestingly, the two functions are identical. char *findIconFile(char *icon, char *pathlist, int type); Search for a file with the specified name (the `icon' argument) along the pathlist given. If found, the complete path name of the file is returned. The type argument corresponds to the second argument of access(2). Typical values are R_OK and X_OK for readability and executability. int ReadASPacket(int fd, unsigned long *header, unsigned long **body); Read a packet from AfterStep. The fd is the file descriptor of the AfterStep-to-module pipe, header should point to an array of MAX_HEADER unsigned longs, and body is the address of a pointer that will be filled with the address of the body of the packet. The caller is responsible for free()ing the packet body area. The routine returns a positive number if the read went fine, zero if the packet was invalid and a negative value if something is very wrong. You should provide a routine with a prototype like void DeadPipe(int nonsense); that will be called by ReadASPacket when the pipe is no longer open. Usually this indicates that AfterStep is exiting. void sleep_a_little(int n); Sleep for the specified amount of microseconds. int GetFdWidth(void); Get the maximum number of files a process can open. char *safemalloc(int size); This is a malloc() that is guaranteed to succeed. int matchWildcards(char *pattern, char *string); Returns a nonzero value if the string matches the pattern. The characters '?' and '*' are wildcards with the usual meanings. int mystrcasecmp(char *a, char *b); int mystrncasecmp(char *a, char *b, int n); These provide the nonstandard but useful str[n]casecmp functions for systems that do not have them. Briefly, they are just like the ANSI str[n]cmp functions but ignore case differences. char *CatString3(char *a, char *b, char *c); Concatenate 3 strings into a single 256 byte global buffer. int mygethostname(char *client, int namelen); Get the name of the host machine if it is available by some means. The string buffer is provided by the caller. int mygetostype(char *buf, int max); Acquire a string indicating the OS type if such information is available. The string buffer is provided by the caller. void CopyString(char **dest, char *source); Allocate memory for a copy of the source string, save the address of the allocate area to *dest, and copy the source string without any leading or trailing whitespace to the allocated area. Compatibility with Fvwm ======================= As AfterStep is derived from Fvwm 1.24, the module interface is very close to that of Fvwm. In practice AfterStep and Fvwm-1 modules are often interchangeable; however problems do occur. I don't know a lot about the Fvwm interface so this section is sketchy at best. Fvwm does not support locking (M_LOCKONSEND) nor shading (M_SHADE, M_UNSHADE). Fvwm does not send icon coordinates in M_DEICONIFY messages. AfterStep does not support any of the Fvwm-2 extensions. More? Further reading =============== The AfterStep source code is the definitive resource. The source is a mess, but in principle it is modular which makes it easier to begin reading and comprehending relevant sections of the code. The module communication takes place in module.c. Obviously existing modules are a good basis for experimentation. See the official ftp site and your favourite search engine to find more modules. If you are just beginning X programming and looking for more depth than is available in the man pages, see the comp.windows.x FAQ for literature references. For the economically challenged, the X source distribution contains dozens of megabytes of documentation. Disclaimer ========== The document contains probably several inaccuracies, mistakes and what not. All responsibility is hereby transferred to the readers of the document. Use it for good.